Commit 8de8bfe8 authored by Guido Gunther's avatar Guido Gunther

Add support to launch favorites

(based on gnome-shell/maynard)

We switch to a private shell protocol from here on.  This might get
folded back into weston's desktop shell protocol at a later point when
we see what else is needed.
parent 9df0d837
......@@ -19,3 +19,10 @@ way to get going is to do the following:
ninja -C _build
ninja -C _build install
## Running
When running from the source tree start *rootston*. Then start *phosh*
using:
_build/run
schemas = ['sm.puri.phosh.gschema.xml']
compiled = gnome.compile_schemas(
build_by_default: true
)
install_data(
schemas,
install_dir: 'share/glib-2.0/schemas'
)
<schemalist>
<schema id="sm.puri.phosh"
path="/sm/puri/phosh/">
<key name="favorites" type="as">
<default>[ 'evince.desktop', 'gnome-chess.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers will be
displayed in the favorites panel along with running applications.
</description>
</key>
</schema>
</schemalist>
......@@ -59,7 +59,15 @@ add_project_arguments(
language: 'c'
)
run_data = configuration_data()
run_data.set('ABS_BUILDDIR', meson.current_build_dir())
configure_file(
input: 'run.in',
output: 'run',
configuration: run_data)
gnome = import('gnome')
subdir('src')
subdir('data')
<protocol name="weston_desktop">
<interface name="weston_desktop_shell" version="1">
<description summary="create desktop widgets and helpers">
Traditional user interfaces can rely on this interface to define the
foundations of typical desktops. Currently it's possible to set up
background, panels and locking surfaces.
<protocol name="phosh_desktop">
<interface name="phosh_mobile_shell" version="1">
<description summary="panel menus and helpers">
Private protocol between phosh and the compositor. Similar to weston's desktop-shell
protocol.
</description>
<request name="set_background">
......@@ -114,22 +112,32 @@
<arg name="position" type="uint"/>
</request>
</interface>
<interface name="weston_screensaver" version="1">
<description summary="interface for implementing screensavers">
Only one client can bind this interface at a time.
</description>
<request name="set_surface">
<description summary="set the surface type as a screensaver">
A screensaver surface is normally hidden, and only visible after an
idle timeout.
<!-- On top of weston's protocol -->
<request name="set_panel_menu">
<description summary="set panel menu">
Add a menu to the panel. Multiple menus can be added.
</description>
<arg name="menu" type="object" interface="wl_surface"/>
</request>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output"/>
<request name="show_panel_menu">
<arg name="menu" type="object" interface="wl_surface"/>
</request>
<request name="hide_panel_menu">
<arg name="menu" type="object" interface="wl_surface"/>
</request>
</interface>
<!--
<enum name="menu_position">
<!- only supported for top panels ->
<entry name="left" value="0"/>
<entry name="right" value="1"/>
</enum>
<request name="set_menu_position">
<arg name="position" type="uint"/>
</request>
-->
</protocol>
#!/bin/sh
ABS_BUILDDIR='@ABS_BUILDDIR@'
export GSETTINGS_SCHEMA_DIR="${ABS_BUILDDIR}/data"
exec "${ABS_BUILDDIR}/src/phosh" $@
/*
* Copyright (C) 2018 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*
* Based on maynard whish is
* Copyright (C) 2013 Collabora Ltd.
* Author: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
*/
#include "config.h"
#include "favorites.h"
#include <gio/gdesktopappinfo.h>
enum {
APP_LAUNCHED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };
struct PhoshFavoritesPrivate {
GtkWidget *grid;
GSettings *settings;
};
G_DEFINE_TYPE_WITH_PRIVATE(PhoshFavorites, phosh_favorites, GTK_TYPE_WINDOW)
static void
term_btn_clicked (PhoshFavorites *self, GtkButton *btn)
{
GError *error = NULL;
g_spawn_command_line_async ("weston-terminal", &error);
if (error)
g_warning ("Could not launch terminal");
g_signal_emit(self, signals[APP_LAUNCHED], 0);
}
static void
favorite_clicked_cb (GtkWidget *widget,
GDesktopAppInfo *info)
{
PhoshFavorites *self;
g_app_info_launch (G_APP_INFO (info), NULL, NULL, NULL);
self = g_object_get_data (G_OBJECT (widget), "favorites");
g_assert (self);
g_signal_emit (self, signals[APP_LAUNCHED], 0);
}
static GtkWidget*
add_favorite (PhoshFavorites *self,
const gchar *favorite)
{
GIcon *icon;
GDesktopAppInfo *info;
GtkWidget *image;
GtkWidget *btn;
info = g_desktop_app_info_new (favorite);
icon = g_app_info_get_icon (G_APP_INFO (info));
image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
btn = gtk_button_new ();
gtk_style_context_remove_class (gtk_widget_get_style_context (btn),
"button");
gtk_style_context_remove_class (gtk_widget_get_style_context (btn),
"image-button");
gtk_style_context_add_class (gtk_widget_get_style_context (btn),
"phosh-favorite");
gtk_button_set_image (GTK_BUTTON (btn), image);
g_object_set (image, "margin", 20, NULL);
g_object_set_data (G_OBJECT (btn), "favorites", self);
g_signal_connect (btn, "clicked", G_CALLBACK (favorite_clicked_cb), info);
return btn;
}
static void
favorites_changed (GSettings *settings,
const gchar *key,
PhoshFavorites *self)
{
PhoshFavoritesPrivate *priv = phosh_favorites_get_instance_private (self);
GtkWidget *btn;
gchar **favorites = g_settings_get_strv (settings, key);
guint top = 1;
/* Remove all favorites first */
gtk_container_foreach (GTK_CONTAINER (priv->grid),
(GtkCallback) gtk_widget_destroy, NULL);
/* Add weston terminal as band aid in case all else fails for now */
btn = gtk_button_new_from_icon_name ("terminal", GTK_ICON_SIZE_DIALOG);
gtk_grid_attach (GTK_GRID (priv->grid), btn, 1, 1, 1, 1);
g_signal_connect_swapped (btn, "clicked", G_CALLBACK (term_btn_clicked), self);
top++;
for (gint i = 0; i < g_strv_length (favorites); i++) {
gchar *fav = favorites[i];
btn = add_favorite (self, fav);
gtk_grid_attach (GTK_GRID (self->priv->grid), btn, 1, top++, 1, 1);
}
g_strfreev (favorites);
}
static void
phosh_favorites_constructed (GObject *object)
{
PhoshFavorites *self = PHOSH_FAVORITES (object);
PhoshFavoritesPrivate *priv = phosh_favorites_get_instance_private (self);
G_OBJECT_CLASS (phosh_favorites_parent_class)->constructed (object);
/* window properties */
gtk_window_set_title (GTK_WINDOW (self), "phosh favorites");
gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
gtk_window_resize (GTK_WINDOW (self), 100, 100);
gtk_widget_realize(GTK_WIDGET (self));
gtk_style_context_add_class (
gtk_widget_get_style_context (GTK_WIDGET (self)),
"phosh-favorites");
priv->grid = gtk_widget_new (GTK_TYPE_GRID, "halign",
GTK_ALIGN_CENTER, "valign",
GTK_ALIGN_CENTER, NULL);
gtk_container_add (GTK_CONTAINER (self), priv->grid);
self->priv->settings = g_settings_new ("sm.puri.phosh");
g_signal_connect (self->priv->settings, "changed::favorites",
G_CALLBACK (favorites_changed), self);
favorites_changed (self->priv->settings, "favorites", self);
}
static void
phosh_favorites_dispose (GObject *object)
{
PhoshFavorites *self = PHOSH_FAVORITES (object);
g_clear_object (&self->priv->settings);
G_OBJECT_CLASS (phosh_favorites_parent_class)->dispose (object);
}
static void
phosh_favorites_class_init (PhoshFavoritesClass *klass)
{
GObjectClass *object_class = (GObjectClass *)klass;
object_class->dispose = phosh_favorites_dispose;
object_class->constructed = phosh_favorites_constructed;
signals[APP_LAUNCHED] = g_signal_new ("app-launched",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
NULL, G_TYPE_NONE, 0);
}
static void
phosh_favorites_init (PhoshFavorites *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
PHOSH_FAVORITES_TYPE,
PhoshFavoritesPrivate);
}
GtkWidget *
phosh_favorites_new (void)
{
return g_object_new (PHOSH_FAVORITES_TYPE, NULL);
}
/*
* Copyright (C) 2018 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*/
#ifndef __PHOSH_FAVORITES_H__
#define __PHOSH_FAVORITES_H__
#include <gtk/gtk.h>
#define PHOSH_FAVORITES_TYPE (phosh_favorites_get_type ())
#define PHOSH_FAVORITES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PHOSH_FAVORITES_TYPE, PhoshFavorites))
#define PHOSH_FAVORITES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOSH_FAVORITES_TYPE, PhoshFavoritesClass))
#define PHOSH_IS_FAVORITES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PHOSH_FAVORITES_TYPE))
#define PHOSH_IS_FAVORITES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOSH_FAVORITES_TYPE))
#define PHOSH_FAVORITES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PHOSH_FAVORITES_TYPE, PhoshFavoritesClass))
typedef struct PhoshFavorites PhoshFavorites;
typedef struct PhoshFavoritesClass PhoshFavoritesClass;
typedef struct PhoshFavoritesPrivate PhoshFavoritesPrivate;
struct PhoshFavorites
{
GtkWindow parent;
PhoshFavoritesPrivate *priv;
};
struct PhoshFavoritesClass
{
GtkWindowClass parent_class;
};
GType phosh_favorites_get_type (void) G_GNUC_CONST;
GtkWidget * phosh_favorites_new (void);
#endif /* __PHOSH_FAVORITES_H__ */
......@@ -32,9 +32,6 @@ struct PhoshLockscreenClass
GtkWindowClass parent_class;
};
#define PHOSH_LOCKSCREEN_WIDTH 56
#define PHOSH_LOCKSCREEN_HEIGHT_RATIO 0.73
GType phosh_lockscreen_get_type (void) G_GNUC_CONST;
GtkWidget * phosh_lockscreen_new (void);
......
......@@ -14,15 +14,17 @@ phosh_resources = gnome.compile_resources(
)
shell_proto_header = gen_scanner_client_header.process('../protocol/weston-desktop-shell.xml')
shell_proto_code = gen_scanner_client_code.process('../protocol/weston-desktop-shell.xml')
shell_proto_header = gen_scanner_client_header.process('../protocol/phosh-mobile-shell.xml')
shell_proto_code = gen_scanner_client_code.process('../protocol/phosh-mobile-shell.xml')
phosh_sources = [
'phosh.c',
'panel.c',
'panel.h',
'favorites.c',
'favorites.h',
'lockscreen.c',
'lockscreen.h',
'panel.c',
'panel.h',
'phosh.c',
shell_proto_header,
shell_proto_code,
phosh_resources,
......
......@@ -18,15 +18,14 @@
#define TOPLEFT_LABEL_TEXT "Librem5 dev board"
enum {
FAVORITE_LAUNCHED,
FAVORITES_ACTIVATED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };
struct PhoshPanelPrivate {
GtkWidget *label_topleft;
GtkWidget *btn_topleft;
GtkWidget *label_clock;
GtkWidget *btn_terminal;
GnomeWallClock *wall_clock;
};
......@@ -34,19 +33,13 @@ struct PhoshPanelPrivate {
G_DEFINE_TYPE_WITH_PRIVATE (PhoshPanel, phosh_panel, GTK_TYPE_WINDOW)
/* FIXME: Temporarily add a term button until we have the menu system */
static void
term_clicked_cb (PhoshPanel *self, GtkButton *btn)
topleft_clicked_cb (PhoshPanel *self, GtkButton *btn)
{
GError *error = NULL;
g_return_if_fail (PHOSH_IS_PANEL (self));
g_return_if_fail (GTK_IS_BUTTON (btn));
g_spawn_command_line_async ("weston-terminal", &error);
if (error)
g_warning ("Could not launch terminal");
g_signal_emit(self, signals[FAVORITES_ACTIVATED], 0);
}
......@@ -76,16 +69,16 @@ phosh_panel_constructed (GObject *object)
G_OBJECT_CLASS (phosh_panel_parent_class)->constructed (object);
gtk_label_set_text (GTK_LABEL (priv->label_topleft), TOPLEFT_LABEL_TEXT);
gtk_button_set_label (GTK_BUTTON (priv->btn_topleft), TOPLEFT_LABEL_TEXT);
priv->wall_clock = g_object_new (GNOME_TYPE_WALL_CLOCK, NULL);
g_signal_connect (priv->wall_clock,
"notify::clock",
G_CALLBACK (wall_clock_notify_cb),
self);
g_signal_connect_object (priv->btn_terminal,
g_signal_connect_object (priv->btn_topleft,
"clicked",
G_CALLBACK (term_clicked_cb),
G_CALLBACK (topleft_clicked_cb),
self,
G_CONNECT_SWAPPED);
......@@ -98,6 +91,14 @@ phosh_panel_constructed (GObject *object)
gtk_widget_get_style_context (GTK_WIDGET (self)),
"phosh-panel");
/* Button properites */
gtk_style_context_remove_class (gtk_widget_get_style_context (priv->btn_topleft),
"button");
gtk_style_context_remove_class (gtk_widget_get_style_context (priv->btn_topleft),
"image-button");
gtk_style_context_add_class (gtk_widget_get_style_context (priv->btn_topleft),
"phosh-panel-btn-top-left");
wall_clock_notify_cb (priv->wall_clock, NULL, self);
}
......@@ -114,7 +115,6 @@ phosh_panel_dispose (GObject *object)
}
static void
phosh_panel_class_init (PhoshPanelClass *klass)
{
......@@ -124,15 +124,14 @@ phosh_panel_class_init (PhoshPanelClass *klass)
object_class->constructed = phosh_panel_constructed;
object_class->dispose = phosh_panel_dispose;
signals[FAVORITE_LAUNCHED] = g_signal_new ("favorite-launched",
signals[FAVORITES_ACTIVATED] = g_signal_new ("favorites-activated",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
NULL, G_TYPE_NONE, 0);
gtk_widget_class_set_template_from_resource (widget_class,
"/sm/puri/phosh/ui/top-panel.ui");
gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, label_topleft);
gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, btn_topleft);
gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, label_clock);
gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, btn_terminal);
}
......
......@@ -14,10 +14,11 @@
#include <gtk/gtk.h>
#include <gdk/gdkwayland.h>
#include "weston-desktop-shell-client-protocol.h"
#include "phosh-mobile-shell-client-protocol.h"
#include "panel.h"
#include "favorites.h"
#include "lockscreen.h"
#include "panel.h"
struct elem {
GtkWidget *window;
......@@ -28,7 +29,7 @@ struct elem {
struct desktop {
struct wl_display *display;
struct wl_registry *registry;
struct weston_desktop_shell *wshell;
struct phosh_mobile_shell *mshell;
struct wl_output *output;
struct wl_seat *seat;
......@@ -36,23 +37,56 @@ struct desktop {
GdkDisplay *gdk_display;
/* Top panel */
struct elem *panel;
/* Background */
struct elem *background;
/* Lockscreen */
struct elem *lockscreen;
gulong unlock_handler_id;
/* Favorites menu */
struct elem *favorites;
gboolean favorites_shown;
};
static void
lockscreen_unlock_cb (struct desktop *desktop, PhoshLockscreen *window)
{
weston_desktop_shell_unlock (desktop->wshell);
phosh_mobile_shell_unlock (desktop->mshell);
g_signal_handler_disconnect (window, desktop->unlock_handler_id);
gtk_widget_destroy (GTK_WIDGET (window));
g_free (desktop->lockscreen);
desktop->lockscreen = NULL;
weston_desktop_shell_unlock(desktop->wshell);
phosh_mobile_shell_unlock(desktop->mshell);
}
static void
favorites_activated_cb (struct desktop *desktop, PhoshLockscreen *window)
{
if (desktop->favorites_shown) {
phosh_mobile_shell_hide_panel_menu(desktop->mshell,
desktop->favorites->surface);
} else {
phosh_mobile_shell_show_panel_menu(desktop->mshell,
desktop->favorites->surface);
}
desktop->favorites_shown = !desktop->favorites_shown;
}
static void
app_launched_cb (struct desktop *desktop, PhoshFavorites *favorites)
{
phosh_mobile_shell_hide_panel_menu(desktop->mshell,
desktop->favorites->surface);
desktop->favorites_shown = FALSE;
}
......@@ -70,7 +104,7 @@ lockscreen_create (struct desktop *desktop)
lockscreen->surface = gdk_wayland_window_get_wl_surface (gdk_window);
weston_desktop_shell_set_lock_surface(desktop->wshell,
phosh_mobile_shell_set_lock_surface(desktop->mshell,
lockscreen->surface);
gtk_widget_show_all (lockscreen->window);
desktop->lockscreen = lockscreen;
......@@ -83,6 +117,32 @@ lockscreen_create (struct desktop *desktop)
}
static void
favorites_create(struct desktop *desktop)
{
struct elem *favorites;
GdkWindow *gdk_window;
favorites = calloc (1, sizeof *favorites);
favorites->window = phosh_favorites_new ();
gdk_window = gtk_widget_get_window (favorites->window);
gdk_wayland_window_set_use_custom_surface (gdk_window);
favorites->surface = gdk_wayland_window_get_wl_surface (gdk_window);
phosh_mobile_shell_set_panel_menu (desktop->mshell,
favorites->surface);
gtk_widget_show_all (favorites->window);
desktop->favorites = favorites;
desktop->favorites_shown = FALSE;
g_signal_connect_swapped (favorites->window,
"app-launched",
G_CALLBACK(app_launched_cb),
desktop);
}
static void
panel_create (struct desktop *desktop)
{
......@@ -95,17 +155,22 @@ panel_create (struct desktop *desktop)
/* set it up as the panel */
gdk_window = gtk_widget_get_window (panel->window);
gdk_wayland_window_set_use_custom_surface (gdk_window);
panel->surface = gdk_wayland_window_get_wl_surface (gdk_window);
weston_desktop_shell_set_user_data (desktop->wshell, desktop);
weston_desktop_shell_set_panel (desktop->wshell, desktop->output,
phosh_mobile_shell_set_user_data (desktop->mshell, desktop);
phosh_mobile_shell_set_panel (desktop->mshell, desktop->output,
panel->surface);
weston_desktop_shell_set_panel_position (desktop->wshell,
WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP);
phosh_mobile_shell_set_panel_position (desktop->mshell,
PHOSH_MOBILE_SHELL_PANEL_POSITION_TOP);
gtk_widget_show_all (panel->window);
desktop->panel = panel;
g_signal_connect_swapped (
panel->window,
"favorites-activated",
G_CALLBACK(favorites_activated_cb),
desktop);
}
......@@ -191,8 +256,8 @@ background_create (struct desktop *desktop)
background->surface = gdk_wayland_window_get_wl_surface (gdk_window);
weston_desktop_shell_set_user_data (desktop->wshell, desktop);
weston_desktop_shell_set_background (desktop->wshell, desktop->output,
phosh_mobile_shell_set_user_data (desktop->mshell, desktop);
phosh_mobile_shell_set_background (desktop->mshell, desktop->output,
background->surface);
desktop->background = background;
......@@ -234,13 +299,13 @@ shell_configure (struct desktop *desktop,
gtk_window_resize (GTK_WINDOW (desktop->panel->window),
width, PHOSH_PANEL_HEIGHT);
weston_desktop_shell_desktop_ready (desktop->wshell);
phosh_mobile_shell_desktop_ready (desktop->mshell);
}
static void
weston_desktop_shell_configure (void *data,
struct weston_desktop_shell *weston_desktop_shell,
phosh_mobile_shell_configure (void *data,
struct phosh_mobile_shell *phosh_mobile_shell,
uint32_t edges,
struct wl_surface *surface,
int32_t width, int32_t height)
......@@ -250,8 +315,8 @@ weston_desktop_shell_configure (void *data,
static void
weston_desktop_shell_prepare_lock_surface (void *data,
struct weston_desktop_shell *weston_desktop_shell)
phosh_mobile_shell_prepare_lock_surface (void *data,
struct phosh_mobile_shell *phosh_mobile_shell)
{
struct desktop *desktop = data;
......@@ -261,18 +326,18 @@ weston_desktop_shell_prepare_lock_surface (void *data,
static void
weston_desktop_shell_grab_cursor (void *data,
struct weston_desktop_shell *weston_desktop_shell,
phosh_mobile_shell_grab_cursor (void *data,
struct phosh_mobile_shell *phosh_mobile_shell,
uint32_t cursor)
{
g_warning("%s not implmented", __func__);
}
static const struct weston_desktop_shell_listener wshell_listener = {
weston_desktop_shell_configure,
weston_desktop_shell_prepare_lock_surface,
weston_desktop_shell_grab_cursor
static const struct phosh_mobile_shell_listener mshell_listener = {
phosh_mobile_shell_configure,
phosh_mobile_shell_prepare_lock_surface,
phosh_mobile_shell_grab_cursor
};
......@@ -285,11 +350,11 @@ registry_handle_global (void *data,
{
struct desktop *d = data;
if (!strcmp (interface, "weston_desktop_shell")) {
d->wshell = wl_registry_bind (registry, name,
&weston_desktop_shell_interface, MIN(version, 1));
weston_desktop_shell_add_listener (d->wshell, &wshell_listener, d);
weston_desktop_shell_set_user_data (d->wshell, d);
if (!strcmp (interface, "phosh_mobile_shell")) {
d->mshell = wl_registry_bind (registry, name,
&phosh_mobile_shell_interface, MIN(version, 1));
phosh_mobile_shell_add_listener (d->mshell, &mshell_listener, d);
phosh_mobile_shell_set_user_data (d->mshell, d);
}
else if (!strcmp (interface, "wl_output")) {
/* TODO: create multiple outputs */
......@@ -337,18 +402,19 @@ int main(int argc, char *argv[])
/* Wait until we have been notified about the compositor,
* shell, and shell helper objects */
if (!desktop->output || !desktop->wshell)
if (!desktop->output || !desktop->mshell)
wl_display_roundtrip (desktop->display);
if (!desktop->output || !desktop->wshell) {
if (!desktop->output || !desktop->mshell) {
g_error ("Could not find output, shell or helper modules\n"
"output: %p, wshell: %p\n",
desktop->output, desktop->wshell);
"output: %p, mshell: %p\n",
desktop->output, desktop->mshell);
return -1;
}
css_setup (desktop);
background_create (desktop);
panel_create (desktop);
favorites_create (desktop);
gtk_main ();
......
.phosh-panel-btn-top-left {
background-image: none;
border-width: 0;
}
......@@ -2,7 +2,7 @@
<!-- Generated with glade 3.20.2 -->
<interface>