Commit d1cf1f02 authored by Guido Gunther's avatar Guido Gunther

Initial commit

parents
_build
*.swp
*~
\#*#
.\#*
# Phosh
a trivial desktop shell devices. This is just so that we can launch a terminal
and try out stuff.
## License
phosh is licensed under the GPLv3+.
## Dependencies
sudo apt-get install libgtk3.0-dev wayland-protocols
## Building
We use the meson (and thereby Ninja) build system for phosh. The quickest
way to get going is to do the following:
meson . _build
ninja -C _build
ninja -C _build install
project('phosh', 'c',
version: '0',
license: 'GPLv3+',
meson_version: '>= 0.40.1',
default_options: [ 'warning_level=1', 'buildtype=debugoptimized', 'c_std=gnu11' ],
)
config_h = configuration_data()
config_h.set_quoted('GETTEXT_PACKAGE', 'phosh')
config_h.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
configure_file(
output: 'config.h',
configuration: config_h,
)
add_project_arguments([
'-DHAVE_CONFIG_H',
'-I' + meson.build_root(),
], language: 'c')
root_inc = include_directories('.')
src_inc = include_directories('src')
cc = meson.get_compiler('c')
global_c_args = []
test_c_args = [
'-Wcast-align',
'-Wdeclaration-after-statement',
['-Werror=format-security', '-Werror=format=2'],
'-Wformat-nonliteral',
'-Wformat-security',
'-Wmissing-include-dirs',
'-Wnested-externs',
'-Wno-missing-field-initializers',
'-Wno-sign-compare',
'-Wno-strict-aliasing',
'-Wno-uninitialized',
'-Wno-unused-parameter',
'-Wpointer-arith',
'-Wredundant-decls',
'-Wshadow',
'-Wswitch-default',
'-Wswitch-enum',
'-Wundef',
]
if get_option('buildtype') != 'plain'
test_c_args += '-fstack-protector-strong'
endif
foreach arg: test_c_args
if cc.has_multi_arguments(arg)
global_c_args += arg
endif
endforeach
add_project_arguments(
global_c_args,
language: 'c'
)
subdir('src')
<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.
</description>
<request name="set_background">
<arg name="output" type="object" interface="wl_output"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="set_panel">
<arg name="output" type="object" interface="wl_output"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="set_lock_surface">
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="unlock"/>
<request name="set_grab_surface">
<description summary="set grab surface">
The surface set by this request will receive a fake
pointer.enter event during grabs at position 0, 0 and is
expected to set an appropriate cursor image as described by
the grab_cursor event sent just before the enter event.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<!-- We'll fold most of wl_shell into this interface and then
they'll share the configure event. -->
<event name="configure">
<arg name="edges" type="uint"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</event>
<event name="prepare_lock_surface">
<description summary="tell the client to create, set the lock surface">
Tell the client we want it to create and set the lock surface, which is
a GUI asking the user to unlock the screen. The lock surface is
announced with 'set_lock_surface'. Whether or not the client actually
implements locking, it MUST send 'unlock' request to let the normal
desktop resume.
</description>
</event>
<event name="grab_cursor">
<description summary="tell client what cursor to show during a grab">
This event will be sent immediately before a fake enter event on the
grab surface.
</description>
<arg name="cursor" type="uint"/>
</event>
<enum name="cursor">
<entry name="none" value="0"/>
<entry name="resize_top" value="1"/>
<entry name="resize_bottom" value="2"/>
<entry name="arrow" value="3"/>
<entry name="resize_left" value="4"/>
<entry name="resize_top_left" value="5"/>
<entry name="resize_bottom_left" value="6"/>
<entry name="move" value="7"/>
<entry name="resize_right" value="8"/>
<entry name="resize_top_right" value="9"/>
<entry name="resize_bottom_right" value="10"/>
<entry name="busy" value="11"/>
</enum>
<request name="desktop_ready">
<description summary="desktop is ready to be shown">
Tell the server, that enough desktop elements have been drawn
to make the desktop look ready for use. During start-up, the
server can wait for this request with a black screen before
starting to fade in the desktop, for instance. If the client
parts of a desktop take a long time to initialize, we avoid
showing temporary garbage.
</description>
</request>
<enum name="panel_position">
<entry name="top" value="0"/>
<entry name="bottom" value="1"/>
<entry name="left" value="2"/>
<entry name="right" value="3"/>
</enum>
<enum name="error">
<entry name="invalid_argument" value="0"
summary="an invalid argument was provided in a request"/>
</enum>
<request name="set_panel_position">
<description summary="set panel position">
Tell the shell which side of the screen the panel is
located. This is so that new windows do not overlap the panel
and maximized windows maximize properly.
</description>
<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.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
</interface>
</protocol>
/*
* Copyright (C) 2018 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*/
#include "config.h"
#include "lockscreen.h"
enum {
LOCKSCREEN_UNLOCK,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };
struct PhoshLockscreenPrivate {
gint _dummy;
};
G_DEFINE_TYPE(PhoshLockscreen, phosh_lockscreen, GTK_TYPE_WINDOW)
static void
phosh_lockscreen_init (PhoshLockscreen *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
PHOSH_LOCKSCREEN_TYPE,
PhoshLockscreenPrivate);
}
/* FIXME: Temporarily add a button until we interface with pam */
static void
btn_clicked (PhoshLockscreen *lockscreen, GtkButton *btn)
{
g_signal_emit(lockscreen, signals[LOCKSCREEN_UNLOCK], 0);
}
static void
phosh_lockscreen_constructed (GObject *object)
{
PhoshLockscreen *self = PHOSH_LOCKSCREEN (object);
GtkWidget *grid;
GtkWidget *button;
GtkWidget *label;
GdkDisplay *display = gdk_display_get_default ();
/* There's no primary monitor on nested wayland so just use the
first one for now */
GdkMonitor *monitor = gdk_display_get_monitor (display, 0);
GdkRectangle geom;
G_OBJECT_CLASS (phosh_lockscreen_parent_class)->constructed (object);
g_return_if_fail(monitor);
gdk_monitor_get_geometry (monitor, &geom);
/* window properties */
gtk_window_set_title (GTK_WINDOW (self), "phosh lockscreen");
gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
gtk_window_resize (GTK_WINDOW (self), geom.width, geom.height);
gtk_widget_realize(GTK_WIDGET (self));
gtk_style_context_add_class (
gtk_widget_get_style_context (GTK_WIDGET (self)),
"phosh-lockscreen");
grid = gtk_widget_new (GTK_TYPE_GRID, "halign", GTK_ALIGN_CENTER, "valign", GTK_ALIGN_CENTER, NULL);
gtk_container_add (GTK_CONTAINER (self), grid);
/* FIXME: just a button for now */
label = gtk_label_new("Click to unlock desktop");
button = gtk_button_new_from_icon_name ("face-surprise-symbolic", GTK_ICON_SIZE_DIALOG);
gtk_grid_attach (GTK_GRID (grid), label, 2, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), button, 2, 3, 1, 1);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (btn_clicked), self);
}
static void
phosh_lockscreen_class_init (PhoshLockscreenClass *klass)
{
GObjectClass *object_class = (GObjectClass *)klass;
object_class->constructed = phosh_lockscreen_constructed;
signals[LOCKSCREEN_UNLOCK] = g_signal_new ("lockscreen-unlock",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
NULL, G_TYPE_NONE, 0);
g_type_class_add_private (object_class, sizeof (PhoshLockscreenPrivate));
}
GtkWidget *
phosh_lockscreen_new (void)
{
return g_object_new (PHOSH_LOCKSCREEN_TYPE, NULL);
}
/*
* Copyright (C) 2018 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*/
#ifndef __PHOSH_LOCKSCREEN_H__
#define __PHOSH_LOCKSCREEN_H__
#include <gtk/gtk.h>
#define PHOSH_LOCKSCREEN_TYPE (phosh_lockscreen_get_type ())
#define PHOSH_LOCKSCREEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PHOSH_LOCKSCREEN_TYPE, PhoshLockscreen))
#define PHOSH_LOCKSCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOSH_LOCKSCREEN_TYPE, PhoshLockscreenClass))
#define PHOSH_IS_LOCKSCREEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PHOSH_LOCKSCREEN_TYPE))
#define PHOSH_IS_LOCKSCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOSH_LOCKSCREEN_TYPE))
#define PHOSH_LOCKSCREEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PHOSH_LOCKSCREEN_TYPE, PhoshLockscreenClass))
typedef struct PhoshLockscreen PhoshLockscreen;
typedef struct PhoshLockscreenClass PhoshLockscreenClass;
typedef struct PhoshLockscreenPrivate PhoshLockscreenPrivate;
struct PhoshLockscreen
{
GtkWindow parent;
PhoshLockscreenPrivate *priv;
};
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);
#endif /* __PHOSH_LOCKSCREEN_H__ */
wl_scanner = find_program('wayland-scanner')
gen_scanner_client_header = generator(wl_scanner,
output: '@BASENAME@-client-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@'])
gen_scanner_client_code = generator(wl_scanner,
output: '@BASENAME@-protocol.c',
arguments: ['code', '@INPUT@', '@OUTPUT@'])
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')
phosh_sources = [
'phosh.c',
'panel.c',
'panel.h',
'lockscreen.c',
'lockscreen.h',
shell_proto_header,
shell_proto_code,
]
phosh_deps = [
dependency('gtk+-3.0'),
dependency('gtk+-wayland-3.0'),
dependency('wayland-client'),
cc.find_library('m', required: false),
cc.find_library('rt', required: false),
]
phosh = executable('phosh', phosh_sources,
dependencies: phosh_deps,
install: true,
install_dir: get_option('libexecdir'),
)
/*
* Copyright (C) 2017 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*
* Based on maynard's panel which is
*
* Copyright (C) 2014 Collabora Ltd. *
* Author: Jonny Lamb <jonny.lamb@collabora.co.uk>
*/
#include "config.h"
#include "panel.h"
enum {
FAVORITE_LAUNCHED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };
struct PhoshPanelPrivate {
gint _dummy;
};
G_DEFINE_TYPE(PhoshPanel, phosh_panel, GTK_TYPE_WINDOW)
static void
phosh_panel_init (PhoshPanel *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
PHOSH_PANEL_TYPE,
PhoshPanelPrivate);
}
/* FIXME: Temporarily add a term button until we have the menu system */
static void
term_clicked (GtkButton *button, gpointer panel)
{
GError *error = NULL;
g_spawn_command_line_async ("weston-terminal", &error);
if (error)
g_warning ("Could not launch terminal");
}
static void
phosh_panel_constructed (GObject *object)
{
PhoshPanel *self = PHOSH_PANEL (object);
GtkWidget *main_box;
GtkWidget *button;
G_OBJECT_CLASS (phosh_panel_parent_class)->constructed (object);
/* window properties */
gtk_window_set_title (GTK_WINDOW (self), "phosh panel");
gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
gtk_widget_realize(GTK_WIDGET (self));
gtk_style_context_add_class (
gtk_widget_get_style_context (GTK_WIDGET (self)),
"phosh-panel");
/* main vbox */
main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (self), main_box);
/* FIXME: temporary terminal button */
button = gtk_button_new_from_icon_name ("terminal", GTK_ICON_SIZE_BUTTON);
gtk_box_pack_end (GTK_BOX (main_box), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked", G_CALLBACK (term_clicked), self);
}
static void
phosh_panel_class_init (PhoshPanelClass *klass)
{
GObjectClass *object_class = (GObjectClass *)klass;
object_class->constructed = phosh_panel_constructed;
signals[FAVORITE_LAUNCHED] = g_signal_new ("favorite-launched",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
NULL, G_TYPE_NONE, 0);
g_type_class_add_private (object_class, sizeof (PhoshPanelPrivate));
}
GtkWidget *
phosh_panel_new (void)
{
return g_object_new (PHOSH_PANEL_TYPE,
NULL);
}
/*
* Copyright (C) 2018 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*/
#ifndef __PHOSH_PANEL_H__
#define __PHOSH_PANEL_H__
#include <gtk/gtk.h>
#define PHOSH_PANEL_TYPE (phosh_panel_get_type ())
#define PHOSH_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PHOSH_PANEL_TYPE, PhoshPanel))
#define PHOSH_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PHOSH_PANEL_TYPE, PhoshPanelClass))
#define PHOSH_IS_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PHOSH_PANEL_TYPE))
#define PHOSH_IS_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOSH_PANEL_TYPE))
#define PHOSH_PANEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PHOSH_PANEL_TYPE, PhoshPanelClass))
typedef struct PhoshPanel PhoshPanel;
typedef struct PhoshPanelClass PhoshPanelClass;
typedef struct PhoshPanelPrivate PhoshPanelPrivate;
struct PhoshPanel
{
GtkWindow parent;
PhoshPanelPrivate *priv;
};
struct PhoshPanelClass
{
GtkWindowClass parent_class;
};
#define PHOSH_PANEL_WIDTH 56
#define PHOSH_PANEL_HEIGHT_RATIO 0.73
GType phosh_panel_get_type (void) G_GNUC_CONST;
GtkWidget * phosh_panel_new (void);
#endif /* __PHOSH_PANEL_H__ */
/*
* Copyright (C) 2017 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*
* Based on maynard's panel which is
*
* Copyright (C) 2014 Collabora Ltd. *
* Author: Jonny Lamb <jonny.lamb@collabora.co.uk>
*/
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkwayland.h>
#include "weston-desktop-shell-client-protocol.h"
#include "panel.h"
#include "lockscreen.h"
struct elem {
GtkWidget *window;
GdkPixbuf *pixbuf;
struct wl_surface *surface;
};
struct desktop {
struct wl_display *display;
struct wl_registry *registry;
struct weston_desktop_shell *wshell;
struct wl_output *output;
struct wl_seat *seat;
struct wl_pointer *pointer;
GdkDisplay *gdk_display;
struct elem *panel;
struct elem *background;
struct elem *lockscreen;
gulong unlock_handler_id;
};
static void
lockscreen_unlock_cb (struct desktop *desktop, PhoshLockscreen *window)
{
weston_desktop_shell_unlock (desktop->wshell);
g_signal_handler_disconnect (window, desktop->unlock_handler_id);
gtk_widget_destroy (GTK_WIDGET (window));
g_free (desktop->lockscreen);
desktop->lockscreen = NULL;
}
static void
lockscreen_create (struct desktop *desktop)
{
struct elem *lockscreen;
GdkWindow *gdk_window;
lockscreen = g_malloc0 (sizeof *lockscreen);
lockscreen->window = phosh_lockscreen_new ();
gdk_window = gtk_widget_get_window (lockscreen->window);
gdk_wayland_window_set_use_custom_surface (gdk_window);
lockscreen->surface = gdk_wayland_window_get_wl_surface (gdk_window);
weston_desktop_shell_set_lock_surface(desktop->wshell,
lockscreen->surface);
gtk_widget_show_all (lockscreen->window);
desktop->lockscreen = lockscreen;
desktop->unlock_handler_id = g_signal_connect_swapped (
lockscreen->window,
"lockscreen-unlock",
G_CALLBACK(lockscreen_unlock_cb),
desktop);
}
static void
panel_create (struct desktop *desktop)
{
struct elem *panel;
GdkWindow *gdk_window;
panel = calloc (1, sizeof *panel);
panel->window = phosh_panel_new ();
/* 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,
panel->surface);
weston_desktop_shell_set_panel_position (desktop->wshell,
WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT);
gtk_widget_show_all (panel->window);
desktop->panel = panel;
}
static GdkPixbuf *
scale_background (GdkPixbuf *original_pixbuf)
{
GdkDisplay *display = gdk_display_get_default ();
/* There's no primary monitor on nested wayland so just use the
first one for now */
GdkMonitor *monitor = gdk_display_get_monitor (display, 0);
GdkRectangle geom;
gint original_width, original_height;
gint final_width, final_height;
gdouble ratio_horizontal, ratio_vertical;
g_return_val_if_fail(monitor, NULL);
gdk_monitor_get_geometry (monitor, &geom);
original_width = gdk_pixbuf_get_width (original_pixbuf);
original_height = gdk_pixbuf_get_height (original_pixbuf);
ratio_horizontal = (double) geom.width / original_width;
ratio_vertical = (double) geom.height / original_height;
final_width = ceil (ratio_horizontal * original_width);
final_height = ceil (ratio_vertical * original_height);
return gdk_pixbuf_scale_simple (original_pixbuf,
final_width, final_height, GDK_INTERP_BILINEAR);
}
static void
background_destroy_cb (GObject *object,
gpointer data)
{
gtk_main_quit ();
}
static gboolean
background_draw_cb (GtkWidget *widget,
cairo_t *cr,
gpointer data)
{
struct desktop *desktop = data;
gdk_cairo_set_source_pixbuf (cr, desktop->background->pixbuf, 0, 0);
cairo_paint (cr);
return TRUE;
}
static void
background_create (struct desktop *desktop)
{
GdkWindow *gdk_window;
struct elem *background;
GdkPixbuf *unscaled_background;
const gchar *xpm_data[] = {"1 1 1 1", "_ c SteelBlue", "_"};
background = calloc (1, sizeof *background);
unscaled_background = gdk_pixbuf_new_from_xpm_data (xpm_data);
background->pixbuf = scale_background (unscaled_background);
g_object_unref (unscaled_background);
background->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (background->window, "destroy",
G_CALLBACK (background_destroy_cb), NULL);
g_signal_connect (background->window, "draw",
G_CALLBACK (background_draw_cb), desktop);
gtk_window_set_title (GTK_WINDOW (background->window), "phosh background");
gtk_window_set_decorated (GTK_WINDOW (background->window), FALSE);
gtk_widget_realize (background->window);
gdk_window = gtk_widget_get_window (background->window);
gdk_wayland_window_set_use_custom_surface (gdk_window);
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,
background->surface);
desktop->background = background;
gtk_widget_show_all (background->window);
}
static void
shell_configure (struct desktop *desktop,
uint32_t edges,
struct wl_surface *surface,
int32_t width, int32_t height)
{
gtk_widget_set_size_request (desktop->background->window,
width, height);
/* FIXME: need to resize panel according to orientation */
gtk_window_resize (GTK_WINDOW (desktop->panel->window),
PHOSH_PANEL_WIDTH, height);
weston_desktop_shell_desktop_ready (desktop->wshell);