Commit 9746500c authored by Bob Ham's avatar Bob Ham
Browse files

Merge branch 'lockscreen-gtkapplication' into 'master'

Display call window over the phosh lockscreen

Closes #41

See merge request Librem5/calls!66
parents 53d7ce00 3201c8e6
...@@ -22,3 +22,24 @@ License: GPL-3+ ...@@ -22,3 +22,24 @@ License: GPL-3+
. .
On Debian systems, the full text of the GNU General Public License version 3 On Debian systems, the full text of the GNU General Public License version 3
can be found in the file `/usr/share/common-licenses/GPL-3'. can be found in the file `/usr/share/common-licenses/GPL-3'.
Files: src/wayland/wlr-layer-shell-unstable-v1.xml
Copytight: Copyright © 2017 Drew DeVault
License: X11
Permission to use, copy, modify, distribute, and sell this software and
its documentation for any purpose is hereby granted without fee, provided
that the above copyright notice appear in all copies and fthat both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of the copyright holders not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission. The copyright holders make
no representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied warranty.
.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
...@@ -24,7 +24,7 @@ project( ...@@ -24,7 +24,7 @@ project(
'c', 'c',
version: '0.0.1', version: '0.0.1',
license: 'GPLv3+', license: 'GPLv3+',
meson_version: '>= 0.46.0', meson_version: '>= 0.47.0',
default_options: [ default_options: [
'warning_level=1', 'warning_level=1',
'buildtype=debugoptimized', 'buildtype=debugoptimized',
......
...@@ -5,3 +5,7 @@ option('gtk_doc', ...@@ -5,3 +5,7 @@ option('gtk_doc',
option('tests', option('tests',
type: 'boolean', value: true, type: 'boolean', value: true,
description: 'Whether to compile unit tests') description: 'Whether to compile unit tests')
option('wayland',
type: 'feature', value: 'enabled',
description: 'Whether to support Wayland lockscreen interaction')
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "calls-call-selector-item.h" #include "calls-call-selector-item.h"
#include "calls-new-call-box.h" #include "calls-new-call-box.h"
#include "calls-enumerate.h" #include "calls-enumerate.h"
#include "calls-wayland-config.h"
#include "util.h" #include "util.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>
...@@ -38,6 +39,11 @@ ...@@ -38,6 +39,11 @@
#define HANDY_USE_UNSTABLE_API #define HANDY_USE_UNSTABLE_API
#include <handy.h> #include <handy.h>
#ifdef CALLS_WAYLAND
#include <gdk/gdkwayland.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "wayland/layersurface.h"
#endif // CALLS_WAYLAND
struct _CallsCallWindow struct _CallsCallWindow
{ {
...@@ -55,6 +61,13 @@ struct _CallsCallWindow ...@@ -55,6 +61,13 @@ struct _CallsCallWindow
GtkButton *show_calls; GtkButton *show_calls;
GtkStack *call_stack; GtkStack *call_stack;
GtkFlowBox *call_selector; GtkFlowBox *call_selector;
#ifdef CALLS_WAYLAND
gboolean screensaver_active;
struct zwlr_layer_shell_v1 *layer_shell_iface;
struct wl_output *output;
GtkWindow *layer_surface;
#endif // CALLS_WAYLAND
}; };
G_DEFINE_TYPE (CallsCallWindow, calls_call_window, GTK_TYPE_APPLICATION_WINDOW); G_DEFINE_TYPE (CallsCallWindow, calls_call_window, GTK_TYPE_APPLICATION_WINDOW);
...@@ -68,11 +81,108 @@ enum { ...@@ -68,11 +81,108 @@ enum {
static GParamSpec *props[PROP_LAST_PROP]; static GParamSpec *props[PROP_LAST_PROP];
#ifdef CALLS_WAYLAND
static void
tear_down_layer_surface (CallsCallWindow *self)
{
GtkWidget *child;
if (!self->layer_surface)
{
return;
}
g_debug ("Tearing down layer surface");
// Remove window content from layer surface
child = gtk_bin_get_child (GTK_BIN (self->layer_surface));
g_object_ref (child);
gtk_container_remove (GTK_CONTAINER (self->layer_surface),
child);
// Add to the call window
gtk_container_add (GTK_CONTAINER (self), child);
g_object_unref (child);
// Destroy layer surface
gtk_widget_destroy (GTK_WIDGET (self->layer_surface));
self->layer_surface = NULL;
}
static void
set_up_layer_surface (CallsCallWindow *self)
{
GtkWidget *child;
if (self->layer_surface
|| !self->layer_shell_iface
|| !self->output)
{
return;
}
g_debug ("Setting up layer surface");
// Create layer surface
self->layer_surface = g_object_new
(PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", self->layer_shell_iface,
"wl-output", self->output,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
"namespace", "calls",
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
NULL);
g_assert (self->layer_surface);
// Remove window content from call window
child = gtk_bin_get_child (GTK_BIN (self));
g_object_ref (child);
gtk_container_remove (GTK_CONTAINER (self), child);
// Add to the layer surface
gtk_container_add (GTK_CONTAINER (self->layer_surface),
child);
g_object_unref (child);
// Show the layer surface
gtk_window_set_keep_above (self->layer_surface, TRUE);
gtk_widget_show (GTK_WIDGET (self->layer_surface));
}
static void
update_layer_surface (CallsCallWindow *self,
guint calls)
{
g_debug ("Updating layer surface");
if (calls == 0)
{
tear_down_layer_surface (self);
}
else if (self->screensaver_active)
{
set_up_layer_surface (self);
}
}
#endif // CALLS_WAYLAND
static void static void
update_visibility (CallsCallWindow *self) update_visibility (CallsCallWindow *self)
{ {
guint calls = g_list_model_get_n_items (G_LIST_MODEL (self->call_holders)); guint calls = g_list_model_get_n_items (G_LIST_MODEL (self->call_holders));
#ifdef CALLS_WAYLAND
update_layer_surface (self, calls);
#endif // CALLS_WAYLAND
gtk_widget_set_visible (GTK_WIDGET (self), calls > 0); gtk_widget_set_visible (GTK_WIDGET (self), calls > 0);
gtk_widget_set_sensitive (GTK_WIDGET (self->show_calls), calls > 1); gtk_widget_set_sensitive (GTK_WIDGET (self->show_calls), calls > 1);
...@@ -347,6 +457,7 @@ set_provider (CallsCallWindow *self, CallsProvider *provider) ...@@ -347,6 +457,7 @@ set_provider (CallsCallWindow *self, CallsProvider *provider)
g_object_unref (params); g_object_unref (params);
} }
static void static void
set_property (GObject *object, set_property (GObject *object,
guint property_id, guint property_id,
...@@ -368,6 +479,163 @@ set_property (GObject *object, ...@@ -368,6 +479,163 @@ set_property (GObject *object,
} }
#ifdef CALLS_WAYLAND
static void
get_screensaver_active (CallsCallWindow *self,
GtkApplication *app)
{
g_object_get (app,
"screensaver-active", &self->screensaver_active,
NULL);
}
static void
notify_screensaver_active_cb (CallsCallWindow *self,
GParamSpec *pspec,
GtkApplication *app)
{
get_screensaver_active (self, app);
g_debug ("Screensaver became %sactive",
self->screensaver_active ? "" : "in");
update_visibility (self);
}
static gboolean
set_up_screensaver (CallsCallWindow *self)
{
GtkApplication *app;
gboolean reg;
app = gtk_window_get_application (GTK_WINDOW (self));
g_assert (app != NULL);
g_object_get (app,
"register-session", &reg,
NULL);
if (!reg)
{
g_warning ("Cannot monitor screensaver state because"
" Application is not set to register with session");
return FALSE;
}
g_signal_connect_swapped (app,
"notify::screensaver-active",
G_CALLBACK (notify_screensaver_active_cb),
self);
get_screensaver_active (self, app);
g_debug ("Screensaver is %sactive at startup",
self->screensaver_active ? "" : "in");
return TRUE;
}
static void
registry_global_cb (void *data,
struct wl_registry *registry,
uint32_t name,
const char *interface,
uint32_t version)
{
CallsCallWindow *self = CALLS_CALL_WINDOW (data);
if (strcmp (interface, zwlr_layer_shell_v1_interface.name) == 0)
{
self->layer_shell_iface = wl_registry_bind
(registry, name, &zwlr_layer_shell_v1_interface, version);
}
else if (strcmp (interface, "wl_output") == 0)
{
// FIXME: Deal with more than one output
if (!self->output)
{
self->output = wl_registry_bind
(registry, name, &wl_output_interface, version);
}
}
}
static const struct wl_registry_listener registry_listener =
{
registry_global_cb,
// FIXME: Add a remove callback
};
static void
set_up_wayland (CallsCallWindow *self)
{
GdkDisplay *gdk_display;
struct wl_display *display;
struct wl_registry *registry;
gdk_display = gdk_display_get_default ();
g_assert (gdk_display != NULL);
if (!GDK_IS_WAYLAND_DISPLAY (gdk_display))
{
g_debug ("GDK display is not a Wayland display");
return;
}
display = gdk_wayland_display_get_wl_display (gdk_display);
g_assert (display != NULL);
registry = wl_display_get_registry (display);
g_assert (registry != NULL);
wl_registry_add_listener (registry, &registry_listener, self);
wl_display_roundtrip (display);
if (!self->layer_shell_iface)
{
g_warning ("Wayland display has no layer shell interface");
}
if (!self->output)
{
g_warning ("Wayland display has no output");
}
}
static void
application_cb (CallsCallWindow *self)
{
gboolean ok;
ok = set_up_screensaver (self);
if (ok)
{
set_up_wayland (self);
}
}
static void
notify (GObject *object,
GParamSpec *pspec)
{
CallsCallWindow *self = CALLS_CALL_WINDOW (object);
const gchar *name;
name = g_param_spec_get_name (pspec);
if (strcmp (name, "application") == 0)
{
application_cb (self);
}
return;
}
#endif // CALLS_WAYLAND
static void static void
constructed (GObject *object) constructed (GObject *object)
{ {
...@@ -379,6 +647,8 @@ constructed (GObject *object) ...@@ -379,6 +647,8 @@ constructed (GObject *object)
(GtkFlowBoxCreateWidgetFunc) call_holders_create_widget_cb, (GtkFlowBoxCreateWidgetFunc) call_holders_create_widget_cb,
NULL, NULL); NULL, NULL);
update_visibility (self);
parent_class->constructed (object); parent_class->constructed (object);
} }
...@@ -389,8 +659,6 @@ calls_call_window_init (CallsCallWindow *self) ...@@ -389,8 +659,6 @@ calls_call_window_init (CallsCallWindow *self)
gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_init_template (GTK_WIDGET (self));
self->call_holders = g_list_store_new (CALLS_TYPE_CALL_HOLDER); self->call_holders = g_list_store_new (CALLS_TYPE_CALL_HOLDER);
update_visibility (self);
} }
...@@ -422,6 +690,12 @@ calls_call_window_class_init (CallsCallWindowClass *klass) ...@@ -422,6 +690,12 @@ calls_call_window_class_init (CallsCallWindowClass *klass)
object_class->constructed = constructed; object_class->constructed = constructed;
object_class->dispose = dispose; object_class->dispose = dispose;
#ifdef CALLS_WAYLAND
// The "application" property is not a construction property so we
// have to wait for it to be set before setting up wayland & co.
object_class->notify = notify;
#endif // CALLS_WAYLAND
props[PROP_PROVIDER] = props[PROP_PROVIDER] =
g_param_spec_object ("provider", g_param_spec_object ("provider",
_("Provider"), _("Provider"),
......
/*
* Copyright (C) 2019 Purism SPC
*
* This file is part of Calls.
*
* Calls is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calls is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef CALLS__WAYLAND_CONFIG_H__
#define CALLS__WAYLAND_CONFIG_H__
#include "config.h"
#if WL_SCANNER_FOUND
#include <gdk/gdk.h>
#ifdef GDK_WINDOWING_WAYLAND
#define CALLS_WAYLAND
#endif // GDK_WINDOWING_WAYLAND
#endif // WL_SCANNER_FOUND
#endif /* CALLS__WAYLAND_CONFIG_H__ */
...@@ -21,9 +21,12 @@ ...@@ -21,9 +21,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
subdir('wayland')
gnome = import('gnome') gnome = import('gnome')
src_include = include_directories('.') src_include = include_directories('.')
calls_includes = [ top_include, src_include ]
calls_deps = [ dependency('gobject-2.0'), calls_deps = [ dependency('gobject-2.0'),
dependency('gtk+-3.0'), dependency('gtk+-3.0'),
...@@ -34,6 +37,11 @@ calls_deps = [ dependency('gobject-2.0'), ...@@ -34,6 +37,11 @@ calls_deps = [ dependency('gobject-2.0'),
dependency('libebook-contacts-1.2'), dependency('libebook-contacts-1.2'),
] ]
if wl_scanner.found()
calls_includes += include_directories('wayland')
calls_deps += dependency('wayland-client', version: '>=1.14')
endif
calls_sources = files(['calls-message-source.c', 'calls-message-source.h', calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
'calls-call.c', 'calls-call.c',
'calls-origin.c', 'calls-origin.h', 'calls-origin.c', 'calls-origin.h',
...@@ -60,6 +68,7 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h', ...@@ -60,6 +68,7 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
calls_config_data = config_data calls_config_data = config_data
calls_config_data.set_quoted('VCS_TAG', '@VCS_TAG@') calls_config_data.set_quoted('VCS_TAG', '@VCS_TAG@')
calls_config_data.set10('WL_SCANNER_FOUND', wl_scanner.found())
config_h_in = configure_file( config_h_in = configure_file(
output: 'config.h.in', output: 'config.h.in',
...@@ -84,8 +93,9 @@ calls_resources = gnome.compile_resources( ...@@ -84,8 +93,9 @@ calls_resources = gnome.compile_resources(
) )
executable('calls', executable('calls',
calls_sources, calls_enum_sources, calls_resources, 'main.c', calls_sources, calls_enum_sources, calls_resources,
wl_proto_sources, wayland_sources, 'main.c',
dependencies : calls_deps, dependencies : calls_deps,
export_dynamic : true, export_dynamic : true,
include_directories : include_directories('..'), include_directories : calls_includes,
install : true) install : true)
/*
* Copyright (C) 2018 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
/*
WARNING: this file is taken directly from phosh, with no modificaions apart from this message. Please update phosh instead of changing this file. Please copy the file back here afterwards, with the same notice.
*/
#define G_LOG_DOMAIN "phosh-layer-surface"
#include "config.h"
#include "layersurface.h"
#include <gdk/gdkwayland.h>
enum {
PHOSH_LAYER_SURFACE_PROP_0,
PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL,
PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT,
PHOSH_LAYER_SURFACE_PROP_ANCHOR,
PHOSH_LAYER_SURFACE_PROP_LAYER,
PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY,
PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE,
PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH,
PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT,
PHOSH_LAYER_SURFACE_PROP_NAMESPACE,
PHOSH_LAYER_SURFACE_PROP_LAST_PROP
};
static GParamSpec *props[PHOSH_LAYER_SURFACE_PROP_LAST_PROP];
enum {
CONFIGURED,
N_SIGNALS
};
static guint signals [N_SIGNALS];
typedef struct {
struct wl_surface *wl_surface;
struct zwlr_layer_surface_v1 *layer_surface;
/* Properties */
guint anchor;
guint layer;
gboolean kbd_interactivity;
gint exclusive_zone;
gint width, height;
gchar *namespace;
struct zwlr_layer_shell_v1 *layer_shell;
struct wl_output *wl_output;
} PhoshLayerSurfacePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (PhoshLayerSurface, phosh_layer_surface, GTK_TYPE_WINDOW)
static void layer_surface_configure(void *data,
struct zwlr_layer_surface_v1 *surface,
uint32_t serial,
uint32_t width,
uint32_t height)
{
PhoshLayerSurface *self = data;
gtk_window_resize (GTK_WINDOW (self), width, height);
zwlr_layer_surface_v1_ack_configure(surface, serial);
g_signal_emit (self, signals[CONFIGURED], 0);
}
static void layer_surface_closed (void *data,
struct zwlr_layer_surface_v1 *surface)
{
PhoshLayerSurface *self = data;
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
g_return_if_fail (priv->layer_surface == surface);
zwlr_layer_surface_v1_destroy(priv->layer_surface);
priv->layer_surface = NULL;
gtk_widget_destroy (GTK_WIDGET (self));
}
static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
.closed = layer_surface_closed,
};
static void
phosh_layer_surface_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PhoshLayerSurface *