Commit 9569fe9d authored by Guido Gunther's avatar Guido Gunther Committed by Guido Gunther
Browse files

Add 'docked' quick setting and manager



Docked manager figures out if a device has enough
hardware around to be treated as desktop/laptop.
If so it sets the corresponding settings like
floating windows, etc.

The quick setting allows to override this.
Signed-off-by: Guido Gunther's avatarGuido Günther <guido.gunther@puri.sm>
parent a94ba5d2
......@@ -50,6 +50,8 @@
<xi:include href="xml/bt-manager.xml"/>
<xi:include href="xml/bt-info.xml"/>
<xi:include href="xml/connectivity-info.xml"/>
<xi:include href="xml/docked-info.xml"/>
<xi:include href="xml/docked-manager.xml"/>
<xi:include href="xml/fader.xml"/>
<xi:include href="xml/favorite-list-model.xml"/>
<xi:include href="xml/feedback-manager.xml"/>
......
......@@ -10,6 +10,7 @@ src/background.c
src/background-manager.c
src/batteryinfo.c
src/bt-info.c
src/docked-info.c
src/fader.c
src/favorite-list-model.c
src/feedbackinfo.c
......
/*
* Copyright (C) 2020 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define G_LOG_DOMAIN "phosh-docked-info"
#include "config.h"
#include "shell.h"
#include "docked-info.h"
#include "docked-manager.h"
/**
* SECTION:docked-info
* @short_description: A widget to display the docked status
* @Title: PhoshDockedInfo
*
* #PhoshDockedInfo displays whether the device is docked
*/
enum {
PROP_0,
PROP_ENABLED,
PROP_PRESENT,
PROP_LAST_PROP
};
static GParamSpec *props[PROP_LAST_PROP];
struct _PhoshDockedInfo {
PhoshStatusIcon parent;
gboolean enabled;
gboolean present;
PhoshDockedManager *manager;
};
G_DEFINE_TYPE (PhoshDockedInfo, phosh_docked_info, PHOSH_TYPE_STATUS_ICON);
static void
phosh_docked_info_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PhoshDockedInfo *self = PHOSH_DOCKED_INFO (object);
switch (property_id) {
case PROP_ENABLED:
g_value_set_boolean (value, self->enabled);
break;
case PROP_PRESENT:
g_value_set_boolean (value, self->present);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
on_docked_mode_enabled (PhoshDockedInfo *self, GParamSpec *pspec, PhoshDockedManager *manager)
{
gboolean enabled;
g_debug ("Updating docked status");
g_return_if_fail (PHOSH_IS_DOCKED_INFO (self));
g_return_if_fail (PHOSH_IS_DOCKED_MANAGER (manager));
enabled = phosh_docked_manager_get_enabled (manager);
if (self->enabled == enabled)
return;
self->enabled = enabled;
phosh_status_icon_set_info (PHOSH_STATUS_ICON (self),
enabled ? _("Docked") : ("Undocked"));
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ENABLED]);
}
static void
on_docked_present (PhoshDockedInfo *self, GParamSpec *pspec, PhoshDockedManager *manager)
{
gboolean can_dock;
g_return_if_fail (PHOSH_IS_DOCKED_INFO (self));
g_return_if_fail (PHOSH_IS_DOCKED_MANAGER (manager));
can_dock = phosh_docked_manager_get_can_dock (manager);
if (self->present == can_dock)
return;
self->present = can_dock;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PRESENT]);
}
static gboolean
on_idle (PhoshDockedInfo *self)
{
g_object_bind_property (self->manager, "icon-name", self, "icon-name",
G_BINDING_SYNC_CREATE);
/* We don't use a binding for self->enabled so we can keep
the property r/o */
g_signal_connect_swapped (self->manager,
"notify::can-dock",
G_CALLBACK (on_docked_present),
self);
on_docked_present (self, NULL, self->manager);
g_signal_connect_swapped (self->manager,
"notify::enabled",
G_CALLBACK (on_docked_mode_enabled),
self);
on_docked_mode_enabled (self, NULL, self->manager);
return FALSE;
}
static void
phosh_docked_info_constructed (GObject *object)
{
PhoshDockedInfo *self = PHOSH_DOCKED_INFO (object);
PhoshShell *shell;
G_OBJECT_CLASS (phosh_docked_info_parent_class)->constructed (object);
shell = phosh_shell_get_default ();
self->manager = g_object_ref (phosh_shell_get_docked_manager (shell));
if (self->manager == NULL) {
g_warning ("Failed to get docked manager");
return;
}
g_idle_add ((GSourceFunc) on_idle, self);
}
static void
phosh_docked_info_dispose (GObject *object)
{
PhoshDockedInfo *self = PHOSH_DOCKED_INFO (object);
if (self->manager) {
g_signal_handlers_disconnect_by_data (self->manager, self);
g_clear_object (&self->manager);
}
G_OBJECT_CLASS (phosh_docked_info_parent_class)->dispose (object);
}
static void
phosh_docked_info_class_init (PhoshDockedInfoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = phosh_docked_info_constructed;
object_class->dispose = phosh_docked_info_dispose;
object_class->get_property = phosh_docked_info_get_property;
props[PROP_ENABLED] =
g_param_spec_boolean ("enabled",
"enabled",
"Whether the docked is enabled",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
props[PROP_PRESENT] =
g_param_spec_boolean ("present",
"Present",
"Whether docked hardware is present",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
phosh_docked_info_init (PhoshDockedInfo *self)
{
phosh_status_icon_set_info (PHOSH_STATUS_ICON (self), _("Undocked"));
}
GtkWidget *
phosh_docked_info_new (void)
{
return g_object_new (PHOSH_TYPE_DOCKED_INFO, NULL);
}
/*
* Copyright (C) 2020 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include "status-icon.h"
G_BEGIN_DECLS
#define PHOSH_TYPE_DOCKED_INFO (phosh_docked_info_get_type ())
G_DECLARE_FINAL_TYPE (PhoshDockedInfo, phosh_docked_info, PHOSH, DOCKED_INFO, PhoshStatusIcon)
GtkWidget * phosh_docked_info_new (void);
G_END_DECLS
/*
* Copyright (C) 2020 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define G_LOG_DOMAIN "phosh-docked-manager"
#include "config.h"
#include "docked-manager.h"
#include "mode-manager.h"
#include "shell.h"
#define DOCKED_DISABLED_ICON "phone-undocked-symbolic"
#define DOCKED_ENABLED_ICON "phone-docked-symbolic"
#define PHOC_KEY_MAXIMIZE "auto-maximize"
#define A11Y_KEY_OSK "screen-keyboard-enabled"
#define WM_KEY_LAYOUT "button-layout"
#define GTK_KEY_IS_PHONE "is-phone"
/**
* SECTION:docked-manager
* @short_description: Handles 'docking" the phone to additional hardware
* @Title: PhoshDockedManager
*
* #PhoshDockedManager allows to dock the phone to additional hardware
* and performs the necessary configuration changes
* (disable OSK, don't maximize windows by default, ...)
*/
enum {
PROP_0,
PROP_MODE_MANAGER,
PROP_ICON_NAME,
PROP_ENABLED,
PROP_CAN_DOCK,
PROP_LAST_PROP
};
static GParamSpec *props[PROP_LAST_PROP];
struct _PhoshDockedManager {
GObject parent;
gboolean enabled;
gboolean can_dock;
const char *icon_name;
PhoshModeManager *mode_manager;
GSettings *phoc_settings;
GSettings *wm_settings;
GSettings *a11y_settings;
GSettings *gtk_settings;
};
G_DEFINE_TYPE (PhoshDockedManager, phosh_docked_manager, G_TYPE_OBJECT);
static void
phosh_docked_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PhoshDockedManager *self = PHOSH_DOCKED_MANAGER (object);
switch (property_id) {
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_CAN_DOCK:
g_value_set_boolean (value, self->can_dock);
break;
case PROP_ENABLED:
g_value_set_boolean (value, self->enabled);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
phosh_docked_manager_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PhoshDockedManager *self = PHOSH_DOCKED_MANAGER (object);
switch (property_id) {
case PROP_MODE_MANAGER:
/* construct only */
self->mode_manager = g_value_dup_object (value);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MODE_MANAGER]);
break;
case PROP_ENABLED:
phosh_docked_manager_set_enabled (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
mode_changed_cb (PhoshDockedManager *self, GParamSpec *pspec, PhoshModeManager *manager)
{
gboolean can_dock = FALSE;
g_return_if_fail (PHOSH_IS_DOCKED_MANAGER (self));
g_return_if_fail (PHOSH_IS_MODE_MANAGER (manager));
g_return_if_fail (self->mode_manager == manager);
/*
* Desktops, laptops and phones with enough external hardware should get floating
* windows, etc
*/
if (phosh_mode_manager_get_mimicry (manager) != PHOSH_MODE_DEVICE_TYPE_PHONE)
can_dock = TRUE;
if (can_dock == self->can_dock)
return;
g_debug ("Docked mode possible: %d", can_dock);
self->can_dock = can_dock;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CAN_DOCK]);
/* Automatically enable/disable docked mode */
phosh_docked_manager_set_enabled (self, can_dock);
}
static void
phosh_docked_manager_constructed (GObject *object)
{
PhoshDockedManager *self = PHOSH_DOCKED_MANAGER (object);
GSettingsSchemaSource *schema_source = g_settings_schema_source_get_default();
g_autoptr (GSettingsSchema) schema = NULL;
G_OBJECT_CLASS (phosh_docked_manager_parent_class)->constructed (object);
self->phoc_settings = g_settings_new ("sm.puri.phoc");
self->a11y_settings = g_settings_new ("org.gnome.desktop.a11y.applications");
self->wm_settings = g_settings_new ("org.gnome.desktop.wm.preferences");
/* This is a downstream only schema that is supposed to go away once GTK
* GTK is adaptive (https://source.puri.sm/Librem5/gtk/-/merge_requests/18) */
schema = g_settings_schema_source_lookup (schema_source, "org.gtk.Settings.Purism", TRUE);
if (schema && g_settings_schema_has_key (schema, GTK_KEY_IS_PHONE))
self->gtk_settings = g_settings_new ("org.gtk.Settings.Purism");
g_object_connect (
self->mode_manager,
"swapped-object-signal::notify::device-type", G_CALLBACK (mode_changed_cb), self,
"swapped-object-signal::notify::mimicry", G_CALLBACK (mode_changed_cb), self,
NULL);
mode_changed_cb (self, NULL, self->mode_manager);
}
static void
phosh_docked_manager_dispose (GObject *object)
{
PhoshDockedManager *self = PHOSH_DOCKED_MANAGER (object);
g_clear_object (&self->phoc_settings);
g_clear_object (&self->a11y_settings);
g_clear_object (&self->wm_settings);
g_clear_object (&self->gtk_settings);
g_clear_object (&self->mode_manager);
G_OBJECT_CLASS (phosh_docked_manager_parent_class)->dispose (object);
}
static void
phosh_docked_manager_class_init (PhoshDockedManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = phosh_docked_manager_constructed;
object_class->dispose = phosh_docked_manager_dispose;
object_class->get_property = phosh_docked_manager_get_property;
object_class->set_property = phosh_docked_manager_set_property;
props[PROP_MODE_MANAGER] =
g_param_spec_object ("mode-manager",
"Mode manager",
"The hw mode object",
PHOSH_TYPE_MODE_MANAGER,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"icon name",
"The docked icon name",
DOCKED_DISABLED_ICON,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
props[PROP_ENABLED] =
g_param_spec_boolean ("enabled",
"enabled",
"Whether docked mode is enabled",
FALSE,
G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
props[PROP_CAN_DOCK] =
g_param_spec_boolean ("can-dock",
"Can dock",
"Whether the device can be docked",
FALSE,
G_PARAM_READABLE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
phosh_docked_manager_init (PhoshDockedManager *self)
{
self->icon_name = DOCKED_DISABLED_ICON;
}
PhoshDockedManager *
phosh_docked_manager_new (PhoshModeManager *mode_manager)
{
return PHOSH_DOCKED_MANAGER (g_object_new (PHOSH_TYPE_DOCKED_MANAGER,
"mode-manager", mode_manager,
NULL));
}
const char *
phosh_docked_manager_get_icon_name (PhoshDockedManager *self)
{
g_return_val_if_fail (PHOSH_IS_DOCKED_MANAGER (self), NULL);
return self->icon_name;
}
gboolean
phosh_docked_manager_get_enabled (PhoshDockedManager *self)
{
g_return_val_if_fail (PHOSH_IS_DOCKED_MANAGER (self), FALSE);
return self->enabled;
}
gboolean
phosh_docked_manager_get_can_dock (PhoshDockedManager *self)
{
g_return_val_if_fail (PHOSH_IS_DOCKED_MANAGER (self), FALSE);
return self->can_dock;
}
void
phosh_docked_manager_set_enabled (PhoshDockedManager *self, gboolean enable)
{
const gchar *icon_name;
g_return_if_fail (PHOSH_IS_DOCKED_MANAGER (self));
if (self->enabled == enable)
return;
g_object_freeze_notify (G_OBJECT (self));
self->enabled = enable;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ENABLED]);
if (enable)
g_settings_reset (self->wm_settings, WM_KEY_LAYOUT);
else
g_settings_set_string (self->wm_settings, WM_KEY_LAYOUT, "appmenu:");
/* we could bind one boolean property via g_settings_bind but that would spread
* mode setup over several places */
g_settings_set_boolean (self->phoc_settings, PHOC_KEY_MAXIMIZE, !enable);
g_settings_set_boolean (self->a11y_settings, A11Y_KEY_OSK, !enable);
if (self->gtk_settings)
g_settings_set_boolean (self->gtk_settings, GTK_KEY_IS_PHONE, !enable);
/* TODO: Other icons for non phones? */
icon_name = enable ? DOCKED_ENABLED_ICON : DOCKED_DISABLED_ICON;
if (icon_name != self->icon_name) {
self->icon_name = icon_name;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON_NAME]);
}
g_object_thaw_notify (G_OBJECT (self));
if (!enable) {
PhoshShell *shell = phosh_shell_get_default ();
PhoshMonitor *monitor = phosh_shell_get_builtin_monitor (shell);
if (monitor)
phosh_shell_set_primary_monitor (shell, monitor);
}
g_debug ("Docked mode %sabled", self->enabled ? "en" : "dis");
}
/*
* Copyright (C) 2020 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <mode-manager.h>
#include <glib-object.h>
G_BEGIN_DECLS
#define PHOSH_TYPE_DOCKED_MANAGER (phosh_docked_manager_get_type ())
G_DECLARE_FINAL_TYPE (PhoshDockedManager, phosh_docked_manager, PHOSH, DOCKED_MANAGER, GObject)
PhoshDockedManager *phosh_docked_manager_new (PhoshModeManager *mode_manager);
const char *phosh_docked_manager_get_icon_name (PhoshDockedManager *self);
gboolean phosh_docked_manager_get_enabled (PhoshDockedManager *self);
gboolean phosh_docked_manager_get_can_dock (PhoshDockedManager *self);
void phosh_docked_manager_set_enabled (PhoshDockedManager *self, gboolean enabled);
G_END_DECLS
......@@ -54,6 +54,10 @@ libphosh_tool_sources = [
'background.h',
'connectivity-info.c',
'connectivity-info.h',
'docked-info.c',
'docked-info.h',
'docked-manager.c',
'docked-manager.h',
'favorite-list-model.c',
'favorite-list-model.h',
'feedback-manager.c',
......
......@@ -11,6 +11,8 @@
#include <glib/gi18n.h>
#include "bt-info.h"
#include "docked-info.h"
#include "mode-manager.h"
#include "shell.h"
#include "settings.h"
#include "quick-setting.h"
......@@ -169,6 +171,24 @@ torch_setting_clicked_cb (PhoshSettings *self)
phosh_torch_manager_toggle (manager);
}
static void
docked_setting_clicked_cb (PhoshSettings *self)
{
PhoshShell *shell;
PhoshDockedManager *manager;
gboolean enabled;
shell = phosh_shell_get_default ();
g_return_if_fail (PHOSH_IS_SHELL (shell));
manager = phosh_shell_get_docked_manager (shell);
g_return_if_fail (PHOSH_IS_DOCKED_MANAGER (manager));
enabled = phosh_docked_manager_get_enabled (manager);
phosh_docked_manager_set_enabled (manager, !enabled);
}
static void
change_volume (PhoshSettings *self,
int steps)
......@@ -498,6 +518,7 @@ phosh_settings_class_init (PhoshSettingsClass *klass)
NULL, G_TYPE_NONE, 0);
g_type_ensure (PHOSH_TYPE_BT_INFO);