diff --git a/data/keyboards/us.yaml b/data/keyboards/us.yaml index 47ecabb5c7ab3319c28ccdd6db31250a19eaba1d..60c9dfb6ff5fe18d74cad4de5677bc0fa7d14fe7 100644 --- a/data/keyboards/us.yaml +++ b/data/keyboards/us.yaml @@ -1,5 +1,5 @@ --- -bounds: { x: 0, y: 1, width: 360, height: 210 } +bounds: { x: 0, y: 1, width: 360, height: 208 } outlines: default: diff --git a/data/keyboards/us_wide.yaml b/data/keyboards/us_wide.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9942182d1bc402102647a04d099ffa04e9efd69f --- /dev/null +++ b/data/keyboards/us_wide.yaml @@ -0,0 +1,85 @@ +--- +bounds: { x: 0, y: 1, width: 540, height: 168 } + +outlines: + default: + bounds: { x: 0, y: 0, width: 54, height: 42 } + altline: + bounds: { x: 0, y: 0, width: 81, height: 42 } + wide: + bounds: { x: 0, y: 0, width: 108, height: 42 } + spaceline: + bounds: { x: 0, y: 0, width: 216, height: 42 } + special: + bounds: { x: 0, y: 0, width: 54, height: 42 } + +views: + base: + - "q w e r t y u i o p" + - "a s d f g h j k l" + - "Shift_L z x c v b n m BackSpace" + - "show_numbers preferences space period Return" + upper: + - "Q W E R T Y U I O P" + - "A S D F G H J K L" + - "Shift_L Z X C V B N M BackSpace" + - "show_numbers preferences space period Return" + numbers: + - "1 2 3 4 5 6 7 8 9 0" + - "@ # $ % & - _ + ( )" + - "show_symbols , \" ' colon ; ! ? BackSpace" + - "show_letters preferences space period Return" + symbols: + - "~ ` | · √ π τ ÷ × ¶" + - "© ® £ € ¥ ^ ° * { }" + - "show_numbers_from_symbols \\ / < > = [ ] BackSpace" + - "show_letters preferences space period Return" + +buttons: + Shift_L: + action: + locking: + lock_view: "upper" + unlock_view: "base" + outline: "altline" + icon: "key-shift" + BackSpace: + outline: "altline" + icon: "edit-clear-symbolic" + preferences: + action: "show_prefs" + outline: "special" + icon: "keyboard-mode-symbolic" + show_numbers: + action: + set_view: "numbers" + outline: "wide" + label: "123" + show_numbers_from_symbols: + action: + set_view: "numbers" + outline: "altline" + label: "123" + show_letters: + action: + set_view: "base" + outline: "wide" + label: "ABC" + show_symbols: + action: + set_view: "symbols" + outline: "altline" + label: "*/=" + period: + outline: "special" + label: "." + space: + outline: "spaceline" + label: " " + Return: + outline: "wide" + icon: "key-enter" + colon: + label: ":" + "\"": + keysym: "quotedbl" diff --git a/data/style.css b/data/style.css index 2ec94bc8fc5677e9ebf16cf2405d4051c26062e3..815c278e4eb4c3ffeeecb6f580fe8ea5e9ebc1d5 100644 --- a/data/style.css +++ b/data/style.css @@ -4,7 +4,7 @@ sq_view { font-family: cantarell, sans-serif; } -sq_button { +sq_view sq_button { color: #deddda; background: #464448; border-style: solid; @@ -14,6 +14,10 @@ sq_button { margin: 4px 2px 4px 2px; } +sq_view.wide sq_button { + margin: 1px 1px 1px 1px; +} + sq_button:active { background: #747077; border-color: #96949d; diff --git a/eek/eek-gtk.h b/eek/eek-gtk.h deleted file mode 100644 index ab35e4e3c1900464d4bb55833baa05cdb43a315a..0000000000000000000000000000000000000000 --- a/eek/eek-gtk.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org> - * Copyright (C) 2010-2011 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#ifndef EEK_GTK_H -#define EEK_GTK_H 1 - -#include "eek.h" -#include "eek-gtk-keyboard.h" - -#endif /* EEK_GTK_H */ diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 2d8aa42f122695e05282a9563687e0a5ae09e5df..2cac6742edaaa8d1cf5c3562e6103c4bba54f418 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -38,7 +38,7 @@ typedef struct _EekRendererPrivate LevelKeyboard *keyboard; PangoContext *pcontext; GtkCssProvider *css_provider; - GtkStyleContext *layout_context; + GtkStyleContext *view_context; GtkStyleContext *button_context; // TODO: maybe move a copy to each button gdouble border_width; @@ -131,7 +131,7 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view) EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); EekColor foreground; - eek_renderer_get_foreground_color (priv->layout_context, &foreground); + eek_renderer_get_foreground_color (priv->view_context, &foreground); EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard)); @@ -142,11 +142,11 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view) }; /* Paint the background covering the entire widget area */ - gtk_render_background (priv->layout_context, + gtk_render_background (priv->view_context, data.cr, 0, 0, priv->allocation_width, priv->allocation_height); - gtk_render_frame (priv->layout_context, + gtk_render_frame (priv->view_context, data.cr, 0, 0, priv->allocation_width, priv->allocation_height); @@ -241,8 +241,7 @@ static void render_button_in_context(EekRenderer *self, if (icon_name) { cairo_surface_t *icon_surface = - eek_renderer_get_icon_surface (icon_name, (gint)(16 / scale), - scale_factor); + eek_renderer_get_icon_surface (icon_name, 16, scale_factor); if (icon_surface) { gint width = cairo_image_surface_get_width (icon_surface); gint height = cairo_image_surface_get_height (icon_surface); @@ -500,7 +499,7 @@ eek_renderer_set_property (GObject *object, GParamSpec *pspec) { EekRendererPrivate *priv = eek_renderer_get_instance_private ( - EEK_RENDERER(object)); + EEK_RENDERER(object)); switch (prop_id) { case PROP_PCONTEXT: @@ -596,7 +595,7 @@ static GType new_type(char *name) { ); } -static GType layout_type() { +static GType view_type() { static GType type = 0; if (!type) { type = new_type("sq_view"); @@ -649,31 +648,6 @@ eek_renderer_init (EekRenderer *self) priv->css_provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (priv->css_provider, "/sm/puri/squeekboard/style.css"); - - /* Create a style context for the layout */ - GtkWidgetPath *path = gtk_widget_path_new(); - gtk_widget_path_append_type(path, layout_type()); - - priv->layout_context = gtk_style_context_new(); - gtk_style_context_set_path(priv->layout_context, path); - gtk_widget_path_unref(path); - gtk_style_context_add_provider (priv->layout_context, - GTK_STYLE_PROVIDER(priv->css_provider), - GTK_STYLE_PROVIDER_PRIORITY_USER); - - /* Create a style context for the buttons */ - path = gtk_widget_path_new(); - gtk_widget_path_append_type(path, layout_type()); - gtk_widget_path_append_type(path, button_type()); - priv->button_context = gtk_style_context_new (); - gtk_style_context_set_path(priv->button_context, path); - gtk_widget_path_unref(path); - - gtk_style_context_set_parent(priv->button_context, priv->layout_context); - gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL); - gtk_style_context_add_provider (priv->button_context, - GTK_STYLE_PROVIDER(priv->css_provider), - GTK_STYLE_PROVIDER_PRIORITY_USER); } static void @@ -702,6 +676,37 @@ eek_renderer_new (LevelKeyboard *keyboard, NULL); EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); priv->keyboard = keyboard; + + /* Create a style context for the layout */ + GtkWidgetPath *path = gtk_widget_path_new(); + gtk_widget_path_append_type(path, view_type()); + + priv->view_context = gtk_style_context_new(); + gtk_style_context_set_path(priv->view_context, path); + gtk_widget_path_unref(path); + if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) { + gtk_style_context_add_class(priv->view_context, "wide"); + } + gtk_style_context_add_provider (priv->view_context, + GTK_STYLE_PROVIDER(priv->css_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + printf("view: %s\n", gtk_style_context_to_string(priv->view_context, GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE)); + + /* Create a style context for the buttons */ + path = gtk_widget_path_new(); + gtk_widget_path_append_type(path, view_type()); + if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) { + gtk_widget_path_iter_add_class(path, -1, "wide"); + } + gtk_widget_path_append_type(path, button_type()); + priv->button_context = gtk_style_context_new (); + gtk_style_context_set_path(priv->button_context, path); + gtk_widget_path_unref(path); + gtk_style_context_set_parent(priv->button_context, priv->view_context); + gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL); + gtk_style_context_add_provider (priv->button_context, + GTK_STYLE_PROVIDER(priv->css_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); return renderer; } diff --git a/eek/eek-xml-layout.c b/eek/eek-xml-layout.c index ce1b1936487d554959439be3839da41ac89d9f30..fe95e10cd71f991b58142455f324879873313fa3 100644 --- a/eek/eek-xml-layout.c +++ b/eek/eek-xml-layout.c @@ -31,9 +31,10 @@ LevelKeyboard * eek_xml_layout_real_create_keyboard (const char *keyboard_type, - EekboardContextService *manager) + EekboardContextService *manager, + enum squeek_arrangement_kind t) { - struct squeek_layout *layout = squeek_load_layout(keyboard_type); + struct squeek_layout *layout = squeek_load_layout(keyboard_type, t); squeek_layout_place_contents(layout); return level_keyboard_new(manager, layout); } diff --git a/eek/eek-xml-layout.h b/eek/eek-xml-layout.h index 9f8ceee34bbd8961c8f8a9e9e0a1246d4a518726..ed0bb3466d3646c3fc48644aefab3245c3917e78 100644 --- a/eek/eek-xml-layout.h +++ b/eek/eek-xml-layout.h @@ -24,10 +24,13 @@ #define EEK_XML_LAYOUT_H 1 #include "eek-types.h" +#include "src/layout.h" G_BEGIN_DECLS + LevelKeyboard * eek_xml_layout_real_create_keyboard (const char *keyboard_type, - EekboardContextService *manager); + EekboardContextService *manager, + enum squeek_arrangement_kind t); G_END_DECLS #endif /* EEK_XML_LAYOUT_H */ diff --git a/eek/layersurface.c b/eek/layersurface.c index c9e93a83b2a514c73fd0a94124494ab4b753ca3d..f74b6bfab9e6885d867fc26c44a64c703c2a795e 100644 --- a/eek/layersurface.c +++ b/eek/layersurface.c @@ -3,15 +3,12 @@ * 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" @@ -27,8 +24,14 @@ enum { PHOSH_LAYER_SURFACE_PROP_LAYER, PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY, PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE, + PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP, + PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM, + PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT, + PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT, PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH, PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT, + PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH, + PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT, PHOSH_LAYER_SURFACE_PROP_NAMESPACE, PHOSH_LAYER_SURFACE_PROP_LAST_PROP }; @@ -50,7 +53,10 @@ typedef struct { guint layer; gboolean kbd_interactivity; gint exclusive_zone; + gint margin_top, margin_bottom; + gint margin_left, margin_right; gint width, height; + gint configured_width, configured_height; gchar *namespace; struct zwlr_layer_shell_v1 *layer_shell; struct wl_output *wl_output; @@ -65,10 +71,24 @@ static void layer_surface_configure(void *data, uint32_t height) { PhoshLayerSurface *self = data; + PhoshLayerSurfacePrivate *priv; + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); gtk_window_resize (GTK_WINDOW (self), width, height); zwlr_layer_surface_v1_ack_configure(surface, serial); + if (priv->configured_height != height) { + priv->configured_height = height; + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT]); + } + + if (priv->configured_width != width) { + priv->configured_width = width; + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH]); + } + + g_debug("Configured %p", self); g_signal_emit (self, signals[CONFIGURED], 0); } @@ -86,8 +106,8 @@ static void layer_surface_closed (void *data, } static struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = layer_surface_configure, - .closed = layer_surface_closed, + .configure = layer_surface_configure, + .closed = layer_surface_closed, }; static void @@ -98,6 +118,7 @@ phosh_layer_surface_set_property (GObject *object, { PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object); PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); + gint width, height; switch (property_id) { case PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL: @@ -113,16 +134,46 @@ phosh_layer_surface_set_property (GObject *object, priv->layer = g_value_get_uint (value); break; case PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY: - priv->kbd_interactivity = g_value_get_boolean (value); + phosh_layer_surface_set_kbd_interactivity (self, g_value_get_boolean (value)); break; case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE: - priv->exclusive_zone = g_value_get_int (value); + phosh_layer_surface_set_exclusive_zone (self, g_value_get_int (value)); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP: + phosh_layer_surface_set_margins (self, + g_value_get_int (value), + priv->margin_right, + priv->margin_bottom, + priv->margin_left); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM: + phosh_layer_surface_set_margins (self, + priv->margin_top, + priv->margin_right, + g_value_get_int (value), + priv->margin_left); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT: + phosh_layer_surface_set_margins (self, + priv->margin_top, + priv->margin_right, + priv->margin_bottom, + g_value_get_int (value)); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT: + phosh_layer_surface_set_margins (self, + priv->margin_top, + g_value_get_int (value), + priv->margin_bottom, + priv->margin_left); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH: - priv->width = g_value_get_uint (value); + width = g_value_get_uint (value); + phosh_layer_surface_set_size(self, width, priv->height); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT: - priv->height = g_value_get_uint (value); + height = g_value_get_uint (value); + phosh_layer_surface_set_size(self, priv->width, height); break; case PHOSH_LAYER_SURFACE_PROP_NAMESPACE: g_free (priv->namespace); @@ -161,7 +212,19 @@ phosh_layer_surface_get_property (GObject *object, g_value_set_boolean (value, priv->kbd_interactivity); break; case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE: - g_value_set_boolean (value, priv->exclusive_zone); + g_value_set_int (value, priv->exclusive_zone); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP: + g_value_set_int (value, priv->margin_top); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM: + g_value_set_int (value, priv->margin_bottom); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT: + g_value_set_int (value, priv->margin_left); + break; + case PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT: + g_value_set_int (value, priv->margin_right); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH: g_value_set_uint (value, priv->width); @@ -169,6 +232,12 @@ phosh_layer_surface_get_property (GObject *object, case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT: g_value_set_uint (value, priv->height); break; + case PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH: + g_value_set_uint (value, priv->configured_width); + break; + case PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT: + g_value_set_uint (value, priv->configured_height); + break; case PHOSH_LAYER_SURFACE_PROP_NAMESPACE: g_value_set_string (value, priv->namespace); break; @@ -221,6 +290,11 @@ on_phosh_layer_surface_mapped (PhoshLayerSurface *self, gpointer unused) zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, priv->exclusive_zone); zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height); zwlr_layer_surface_v1_set_anchor(priv->layer_surface, priv->anchor); + zwlr_layer_surface_v1_set_margin(priv->layer_surface, + priv->margin_top, + priv->margin_right, + priv->margin_bottom, + priv->margin_left); zwlr_layer_surface_v1_set_keyboard_interactivity(priv->layer_surface, priv->kbd_interactivity); zwlr_layer_surface_v1_add_listener(priv->layer_surface, &layer_surface_listener, @@ -262,6 +336,8 @@ phosh_layer_surface_constructed (GObject *object) g_signal_connect (self, "unmap", G_CALLBACK (on_phosh_layer_surface_unmapped), NULL); + + G_OBJECT_CLASS (phosh_layer_surface_parent_class)->constructed (object); } @@ -332,7 +408,7 @@ phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass) "Keyboard interactivity", "Whether the surface interacts with the keyboard", FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE] = g_param_spec_int ( @@ -342,7 +418,47 @@ phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass) -1, G_MAXINT, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT] = + g_param_spec_int ( + "margin-left", + "Left margin", + "Distance away from the left anchor point", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT] = + g_param_spec_int ( + "margin-right", + "Right margin", + "Distance away from the right anchor point", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP] = + g_param_spec_int ( + "margin-top", + "Top margin", + "Distance away from the top anchor point", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM] = + g_param_spec_int ( + "margin-bottom", + "Bottom margin", + "Distance away from the bottom anchor point", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH] = g_param_spec_uint ( @@ -352,7 +468,7 @@ phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass) 0, G_MAXUINT, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT] = g_param_spec_uint ( @@ -362,7 +478,28 @@ phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass) 0, G_MAXUINT, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + + props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH] = + g_param_spec_uint ( + "configured-width", + "Configured width", + "The width of the layer surface set by the compositor", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT] = + g_param_spec_uint ( + "configured-height", + "Configured height", + "The height of the layer surface set by the compositor", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_NAMESPACE] = g_param_spec_string ( @@ -438,3 +575,148 @@ phosh_layer_surface_get_wl_surface(PhoshLayerSurface *self) priv = phosh_layer_surface_get_instance_private (self); return priv->wl_surface; } + +/** + * phosh_layer_surface_set_size: + * + * Set the size of a layer surface. A value of '-1' indicates 'use old value' + */ +void +phosh_layer_surface_set_size(PhoshLayerSurface *self, gint width, gint height) +{ + PhoshLayerSurfacePrivate *priv; + gint old_width, old_height; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + if (priv->height == height && priv->width == width) + return; + + old_width = priv->width; + old_height = priv->height; + + if (width != -1) + priv->width = width; + + if (height != -1) + priv->height = height; + + if (gtk_widget_get_mapped (GTK_WIDGET (self))) { + zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height); + } + + if (priv->height != old_height) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT]); + + if (priv->width != old_width) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH]); +} + +/** + * phosh_layer_surface_set_margins: + * + * Set anchor margins of a layer surface. + */ +void +phosh_layer_surface_set_margins(PhoshLayerSurface *self, gint top, gint right, gint bottom, gint left) +{ + PhoshLayerSurfacePrivate *priv; + gint old_top, old_bottom, old_left, old_right; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + old_top = priv->margin_top; + old_left = priv->margin_left; + old_right = priv->margin_right; + old_bottom = priv->margin_bottom; + + if (old_top == top && old_left == left && old_right == right && old_bottom == bottom) + return; + + priv->margin_top = top; + priv->margin_left = left; + priv->margin_right = right; + priv->margin_bottom = bottom; + + if (priv->layer_surface) + zwlr_layer_surface_v1_set_margin(priv->layer_surface, top, right, bottom, left); + + if (old_top != top) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP]); + if (old_bottom != bottom) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM]); + if (old_left != left) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT]); + if (old_right != right) + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT]); +} + +/** + * phosh_layer_surface_set_exclusive_zone: + * + * Set exclusive zone of a layer surface. + */ +void +phosh_layer_surface_set_exclusive_zone(PhoshLayerSurface *self, gint zone) +{ + PhoshLayerSurfacePrivate *priv; + gint old_zone; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + old_zone = priv->exclusive_zone; + + if (old_zone == zone) + return; + + priv->exclusive_zone = zone; + + if (priv->layer_surface) + zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, zone); + + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE]); +} + +/** + * phosh_layer_surface_set_keyboard_interactivity: + * + * Set keyboard ineractivity a layer surface. + */ +void +phosh_layer_surface_set_kbd_interactivity (PhoshLayerSurface *self, gboolean interactivity) +{ + PhoshLayerSurfacePrivate *priv; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + if (priv->kbd_interactivity == interactivity) + return; + + priv->kbd_interactivity = interactivity; + + if (priv->layer_surface) + zwlr_layer_surface_v1_set_keyboard_interactivity (priv->layer_surface, interactivity); + + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY]); +} + +/** + * phosh_layer_surface_wl_surface_commit: + * + * Forces a commit of layer surface's state. + */ +void +phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self) +{ + PhoshLayerSurfacePrivate *priv; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + if (priv->wl_surface) + wl_surface_commit (priv->wl_surface); +} diff --git a/eek/layersurface.h b/eek/layersurface.h index 089ebbd4cd54fdca192a2f9dad09b01e1e7e00a5..8569d48812980c095a2f9af0d01ecb1f6d42165d 100644 --- a/eek/layersurface.h +++ b/eek/layersurface.h @@ -11,7 +11,6 @@ WARNING: this file is taken directly from phosh, with no modificaions apart from */ - #pragma once #include <gtk/gtk.h> @@ -39,3 +38,16 @@ GtkWidget *phosh_layer_surface_new (gpointer layer_shell, gpointer wl_output); struct zwlr_layer_surface_v1 *phosh_layer_surface_get_layer_surface(PhoshLayerSurface *self); struct wl_surface *phosh_layer_surface_get_wl_surface(PhoshLayerSurface *self); +void phosh_layer_surface_set_size(PhoshLayerSurface *self, + gint width, + gint height); +void phosh_layer_surface_set_margins(PhoshLayerSurface *self, + gint top, + gint right, + gint bottom, + gint left); +void phosh_layer_surface_set_exclusive_zone(PhoshLayerSurface *self, + gint zone); +void phosh_layer_surface_set_kbd_interactivity(PhoshLayerSurface *self, + gboolean interactivity); +void phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self); diff --git a/eekboard/eekboard-context-service.c b/eekboard/eekboard-context-service.c index 746eeb6a19dabd4cd711e3ae8ddf74734077c37a..17410c20b52696ac3d5b25b2551b7c08627f489d 100644 --- a/eekboard/eekboard-context-service.c +++ b/eekboard/eekboard-context-service.c @@ -41,12 +41,10 @@ #include "wayland.h" #include "eek/eek-xml-layout.h" +#include "src/server-context-service.h" #include "eekboard/eekboard-context-service.h" -#define CSW 640 -#define CSH 480 - enum { PROP_0, // Magic: without this, keyboard is not useable in g_object_notify PROP_KEYBOARD, @@ -73,10 +71,6 @@ struct _EekboardContextServicePrivate { LevelKeyboard *keyboard; // currently used keyboard GHashTable *keyboard_hash; // a table of available keyboards, per layout - // TODO: make use of repeating buttons - guint repeat_timeout_id; - gboolean repeat_triggered; - GSettings *settings; uint32_t hint; uint32_t purpose; @@ -86,9 +80,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_ static LevelKeyboard * eekboard_context_service_real_create_keyboard (EekboardContextService *self, - const gchar *keyboard_type) + const gchar *keyboard_type, + enum squeek_arrangement_kind t) { - LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self); + LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self, t); if (!keyboard) { g_error("Failed to create a keyboard"); } @@ -231,8 +226,8 @@ settings_get_layout(GSettings *settings, char **type, char **layout) g_variant_unref(inputs); } -static void -settings_update_layout(EekboardContextService *context) +void +eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t) { g_autofree gchar *keyboard_type = NULL; g_autofree gchar *keyboard_layout = NULL; @@ -262,7 +257,7 @@ settings_update_layout(EekboardContextService *context) GUINT_TO_POINTER(keyboard_id)); // create a keyboard if (!keyboard) { - keyboard = eekboard_context_service_real_create_keyboard(context, keyboard_layout); + keyboard = eekboard_context_service_real_create_keyboard(context, keyboard_layout, t); g_hash_table_insert (context->priv->keyboard_hash, GUINT_TO_POINTER(keyboard_id), @@ -272,11 +267,14 @@ settings_update_layout(EekboardContextService *context) } // set as current context->priv->keyboard = keyboard; - // TODO: this used to save the group, why? - //group = eek_element_get_group (EEK_ELEMENT(context->priv->keyboard)); + g_object_notify (G_OBJECT(context), "keyboard"); } +static void update_layout_and_type(EekboardContextService *context) { + eekboard_context_service_update_layout(context, server_context_service_get_layout_type(context)); +} + static gboolean settings_handle_layout_changed(GSettings *s, gpointer keys, gint n_keys, @@ -285,7 +283,7 @@ settings_handle_layout_changed(GSettings *s, (void)keys; (void)n_keys; EekboardContextService *context = user_data; - settings_update_layout(context); + update_layout_and_type(context); return TRUE; } @@ -299,7 +297,7 @@ eekboard_context_service_constructed (GObject *object) if (!context->virtual_keyboard) { g_error("Programmer error: Failed to receive a virtual keyboard instance"); } - settings_update_layout(context); + update_layout_and_type(context); } static void @@ -518,6 +516,6 @@ void eekboard_context_service_set_hint_purpose(EekboardContextService *context, if (priv->hint != hint || priv->purpose != purpose) { priv->hint = hint; priv->purpose = purpose; - settings_update_layout(context); + update_layout_and_type(context); } } diff --git a/eekboard/eekboard-context-service.h b/eekboard/eekboard-context-service.h index b8db74a5092cde48c447aad6daf8adf23e4db39a..38e4a9f24e30732b7c0b37383ed6376a84bcecf6 100644 --- a/eekboard/eekboard-context-service.h +++ b/eekboard/eekboard-context-service.h @@ -105,6 +105,7 @@ void eekboard_context_service_set_keymap(EekboardContextService *context, void eekboard_context_service_set_hint_purpose(EekboardContextService *context, uint32_t hint, uint32_t purpose); - +void +eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t); G_END_DECLS #endif /* EEKBOARD_CONTEXT_SERVICE_H */ diff --git a/meson.build b/meson.build index f6639117cd4f3c9c69d0bdafba05c8261b3d6f1f..11c26d63e4500073c066e2dc6cd5cbf24330bff1 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,7 @@ add_project_arguments( '-Werror=maybe-uninitialized', '-Werror=missing-field-initializers', '-Werror=incompatible-pointer-types', + '-Werror=int-conversion', ], language: 'c' ) diff --git a/src/data.rs b/src/data.rs index 61760799bf755a4311d097547a0254fd4bf90289..0066b8ec563312dac1aa8a9c24107e16955ebc10 100644 --- a/src/data.rs +++ b/src/data.rs @@ -17,6 +17,7 @@ use ::keyboard::{ KeyState, PressType, generate_keymap, generate_keycodes, FormattingError }; +use ::layout::ArrangementKind; use ::resources; use ::util::c::as_str; use ::util::hash_map_map; @@ -33,15 +34,24 @@ use serde::Deserialize; pub mod c { use super::*; use std::os::raw::c_char; - + #[no_mangle] pub extern "C" - fn squeek_load_layout(name: *const c_char) -> *mut ::layout::Layout { + fn squeek_load_layout( + name: *const c_char, + type_: u32, + ) -> *mut ::layout::Layout { + let type_ = match type_ { + 0 => ArrangementKind::Base, + 1 => ArrangementKind::Wide, + _ => panic!("Bad enum value"), + }; let name = as_str(&name) .expect("Bad layout name") .expect("Empty layout name"); - let layout = load_layout_with_fallback(name); + let (kind, layout) = load_layout_data_with_fallback(&name, type_); + let layout = ::layout::Layout::new(layout, kind); Box::into_raw(Box::new(layout)) } } @@ -84,28 +94,66 @@ impl fmt::Display for DataSource { } /// Lists possible sources, with 0 as the most preferred one +/// Trying order: native lang of the right kind, native base, +/// fallback lang of the right kind, fallback base fn list_layout_sources( name: &str, - keyboards_path: Option<PathBuf> -) -> Vec<DataSource> { + kind: ArrangementKind, + keyboards_path: Option<PathBuf>, +) -> Vec<(ArrangementKind, DataSource)> { let mut ret = Vec::new(); - if let Some(path) = keyboards_path.clone() { - ret.push(DataSource::File(path.join(name).with_extension("yaml"))) - } - - ret.push(DataSource::Resource(name.to_owned())); + { + fn name_with_arrangement(name: String, kind: &ArrangementKind) + -> String + { + match kind { + ArrangementKind::Base => name, + ArrangementKind::Wide => name + "_wide", + } + } + + let mut add_by_name = |name: &str, kind: &ArrangementKind| { + if let Some(path) = keyboards_path.clone() { + ret.push(( + kind.clone(), + DataSource::File( + path.join(name.to_owned()).with_extension("yaml") + ) + )) + } + + ret.push(( + kind.clone(), + DataSource::Resource(name.into()) + )); + }; + + match &kind { + ArrangementKind::Base => {}, + kind => add_by_name( + &name_with_arrangement(name.into(), &kind), + &kind, + ), + }; + + add_by_name(name, &ArrangementKind::Base); + + match &kind { + ArrangementKind::Base => {}, + kind => add_by_name( + &name_with_arrangement(FALLBACK_LAYOUT_NAME.into(), &kind), + &kind, + ), + }; - if let Some(path) = keyboards_path.clone() { - ret.push(DataSource::File( - path.join(FALLBACK_LAYOUT_NAME).with_extension("yaml") - )) + add_by_name(FALLBACK_LAYOUT_NAME, &ArrangementKind::Base); } - - ret.push(DataSource::Resource(FALLBACK_LAYOUT_NAME.to_owned())); ret } -fn load_layout(source: DataSource) -> Result<::layout::Layout, LoadError> { +fn load_layout_data(source: DataSource) + -> Result<::layout::LayoutData, LoadError> +{ match source { DataSource::File(path) => { Layout::from_file(path.clone()) @@ -123,15 +171,16 @@ fn load_layout(source: DataSource) -> Result<::layout::Layout, LoadError> { } } -fn load_layout_with_fallback( - name: &str -) -> ::layout::Layout { +fn load_layout_data_with_fallback( + name: &str, + kind: ArrangementKind, +) -> (ArrangementKind, ::layout::LayoutData) { let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR") .map(PathBuf::from) .or_else(|| xdg::data_path("squeekboard/keyboards")); - for source in list_layout_sources(name, path) { - let layout = load_layout(source.clone()); + for (kind, source) in list_layout_sources(name, kind, path) { + let layout = load_layout_data(source.clone()); match layout { Err(e) => match (e, source) { ( @@ -146,7 +195,7 @@ fn load_layout_with_fallback( source, e ), }, - Ok(layout) => return layout, + Ok(layout) => return (kind, layout), } } @@ -256,7 +305,9 @@ impl Layout { serde_yaml::from_reader(infile).map_err(Error::Yaml) } - pub fn build(self) -> Result<::layout::Layout, FormattingError> { + pub fn build(self) + -> Result<::layout::LayoutData, FormattingError> + { let button_names = self.views.values() .flat_map(|rows| { rows.iter() @@ -363,15 +414,12 @@ impl Layout { )}) ); - Ok(::layout::Layout { - current_view: "base".into(), + Ok(::layout::LayoutData { views: views, keymap_str: { CString::new(keymap_str) .expect("Invalid keymap string generated") }, - locked_keys: HashSet::new(), - pressed_keys: HashSet::new(), }) } } @@ -665,13 +713,16 @@ mod tests { /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME #[test] fn fallbacks_order() { - let sources = list_layout_sources("nb", None); + let sources = list_layout_sources("nb", ArrangementKind::Base, None); assert_eq!( sources, vec!( - DataSource::Resource("nb".into()), - DataSource::Resource(FALLBACK_LAYOUT_NAME.into()), + (ArrangementKind::Base, DataSource::Resource("nb".into())), + ( + ArrangementKind::Base, + DataSource::Resource(FALLBACK_LAYOUT_NAME.into()) + ), ) ); } diff --git a/src/layout.h b/src/layout.h index d820d229d5e5dddf651b4f1adb9a40c7e9f8977e..ea6ee949558a8671321083faf86dd7b169184233 100644 --- a/src/layout.h +++ b/src/layout.h @@ -9,6 +9,11 @@ #include "src/keyboard.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" +enum squeek_arrangement_kind { + ARRANGEMENT_KIND_BASE = 0, + ARRANGEMENT_KIND_WIDE = 1, +}; + struct squeek_button; struct squeek_row; struct squeek_view; @@ -52,8 +57,9 @@ void squeek_layout_place_contents(struct squeek_layout*); struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*); -struct squeek_layout *squeek_load_layout(const char *type); +struct squeek_layout *squeek_load_layout(const char *name, uint32_t type); const char *squeek_layout_get_keymap(const struct squeek_layout*); +enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *); void squeek_layout_free(struct squeek_layout*); void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp, EekGtkKeyboard *ui_keyboard); diff --git a/src/layout.rs b/src/layout.rs index c87b89bf752fd741fc2b571c0d4a2be0e38c9f15..442dc893549d89ba182778fe0aa0e14067ba4a90 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -203,6 +203,13 @@ pub mod c { layout.keymap_str.as_ptr() } + #[no_mangle] + pub extern "C" + fn squeek_layout_get_kind(layout: *const Layout) -> u32 { + let layout = unsafe { &*layout }; + layout.kind.clone() as u32 + } + #[no_mangle] pub extern "C" fn squeek_layout_free(layout: *mut Layout) { @@ -687,10 +694,18 @@ impl View { } } +/// The physical characteristic of layout for the purpose of styling +#[derive(Clone, PartialEq, Debug)] +pub enum ArrangementKind { + Base = 0, + Wide = 1, +} + // TODO: split into sth like // Arrangement (views) + details (keymap) + State (keys) /// State of the UI, contains the backend as well pub struct Layout { + pub kind: ArrangementKind, pub current_view: String, // Views own the actual buttons which have state // Maybe they should own UI only, @@ -706,6 +721,12 @@ pub struct Layout { pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>, } +/// A builder structure for picking up layout data from storage +pub struct LayoutData { + pub views: HashMap<String, Box<View>>, + pub keymap_str: CString, +} + struct NoSuchView; // Unfortunately, changes are not atomic due to mutability :( @@ -713,6 +734,16 @@ struct NoSuchView; // The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special. // Cloning could also be used. impl Layout { + pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout { + Layout { + kind, + current_view: "base".to_owned(), + views: data.views, + keymap_str: data.keymap_str, + pressed_keys: HashSet::new(), + locked_keys: HashSet::new(), + } + } fn get_current_view(&self) -> &Box<View> { self.views.get(&self.current_view).expect("Selected nonexistent view") } diff --git a/src/lib.rs b/src/lib.rs index e5aded47f540263502759d2e1fe068f105984a04..d46523497085423750a024cd63f3ecbee6efeb26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod float_ord; pub mod imservice; mod keyboard; mod layout; +mod outputs; mod resources; mod submission; mod util; diff --git a/src/outputs.h b/src/outputs.h new file mode 100644 index 0000000000000000000000000000000000000000..38a207f67d10c69a3bb57fbc48f3e06d3ac36ad0 --- /dev/null +++ b/src/outputs.h @@ -0,0 +1,13 @@ +#ifndef __OUTPUTS_H +#define __OUTPUTS_H + +#include "wayland-client-protocol.h" + +struct squeek_outputs; + +struct squeek_outputs *squeek_outputs_new(); +void squeek_outputs_free(struct squeek_outputs*); +void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output); +struct wl_output *squeek_outputs_get_current(struct squeek_outputs*); +int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output); +#endif diff --git a/src/outputs.rs b/src/outputs.rs new file mode 100644 index 0000000000000000000000000000000000000000..9692e954950b3b011d0c572eee5ad0cc62c1964b --- /dev/null +++ b/src/outputs.rs @@ -0,0 +1,322 @@ +/*! Managing Wayland outputs */ + +use std::vec::Vec; + + +/// Gathers stuff defined in C or called by C +pub mod c { + use super::*; + + use std::os::raw::{ c_char, c_void }; + + use ::util::c::COpaquePtr; + + // Defined in C + + #[repr(transparent)] + #[derive(Clone, PartialEq)] + pub struct WlOutput(*const c_void); + + #[repr(C)] + struct WlOutputListener<T: COpaquePtr> { + geometry: extern fn( + T, // data + WlOutput, + i32, // x + i32, // y + i32, // physical_width + i32, // physical_height + i32, // subpixel + *const c_char, // make + *const c_char, // model + i32, // transform + ), + mode: extern fn( + T, // data + WlOutput, + u32, // flags + i32, // width + i32, // height + i32, // refresh + ), + done: extern fn( + T, // data + WlOutput, + ), + scale: extern fn( + T, // data + WlOutput, + i32, // factor + ), + } + + bitflags!{ + /// Map to `wl_output.mode` values + pub struct Mode: u32 { + const NONE = 0x0; + const CURRENT = 0x1; + const PREFERRED = 0x2; + } + } + + /// Map to `wl_output.transform` values + #[derive(Clone)] + pub enum Transform { + Normal = 0, + Rotated90 = 1, + Rotated180 = 2, + Rotated270 = 3, + Flipped = 4, + FlippedRotated90 = 5, + FlippedRotated180 = 6, + FlippedRotated270 = 7, + } + + impl Transform { + fn from_u32(v: u32) -> Option<Transform> { + use self::Transform::*; + match v { + 0 => Some(Normal), + 1 => Some(Rotated90), + 2 => Some(Rotated180), + 3 => Some(Rotated270), + 4 => Some(Flipped), + 5 => Some(FlippedRotated90), + 6 => Some(FlippedRotated180), + 7 => Some(FlippedRotated270), + _ => None, + } + } + } + + extern "C" { + // Rustc wrongly assumes + // that COutputs allows C direct access to the underlying RefCell + #[allow(improper_ctypes)] + fn squeek_output_add_listener( + wl_output: WlOutput, + listener: *const WlOutputListener<COutputs>, + data: COutputs, + ) -> i32; + } + + type COutputs = ::util::c::Wrapped<Outputs>; + + // Defined in Rust + + extern fn outputs_handle_geometry( + outputs: COutputs, + wl_output: WlOutput, + _x: i32, _y: i32, + _phys_width: i32, _phys_height: i32, + _subpixel: i32, + _make: *const c_char, _model: *const c_char, + transform: i32, + ) { + let transform = Transform::from_u32(transform as u32).unwrap_or_else( + || { + eprintln!( + "Warning: received invalid wl_output.transform value" + ); + Transform::Normal + } + ); + + let outputs = outputs.clone_ref(); + let mut collection = outputs.borrow_mut(); + let output_state: Option<&mut OutputState> + = find_output_mut(&mut collection, wl_output) + .map(|o| &mut o.pending); + match output_state { + Some(state) => { state.transform = Some(transform) }, + None => eprintln!("Wayland error: Got mode on unknown output"), + }; + } + + extern fn outputs_handle_mode( + outputs: COutputs, + wl_output: WlOutput, + flags: u32, + width: i32, + height: i32, + _refresh: i32, + ) { + let flags = Mode::from_bits(flags).unwrap_or_else(|| { + eprintln!("Warning: received invalid wl_output.mode flags"); + Mode::NONE + }); + let outputs = outputs.clone_ref(); + let mut collection = outputs.borrow_mut(); + let output_state: Option<&mut OutputState> + = find_output_mut(&mut collection, wl_output) + .map(|o| &mut o.pending); + match output_state { + Some(state) => { + if flags.contains(Mode::CURRENT) { + state.current_mode = Some(super::Mode { width, height}); + } + }, + None => eprintln!("Wayland error: Got mode on unknown output"), + }; + } + + extern fn outputs_handle_done( + outputs: COutputs, + wl_output: WlOutput, + ) { + let outputs = outputs.clone_ref(); + let mut collection = outputs.borrow_mut(); + let output = find_output_mut(&mut collection, wl_output); + match output { + Some(output) => { output.current = output.pending.clone(); } + None => eprintln!("Wayland error: Got done on unknown output"), + }; + } + + extern fn outputs_handle_scale( + outputs: COutputs, + wl_output: WlOutput, + factor: i32, + ) { + let outputs = outputs.clone_ref(); + let mut collection = outputs.borrow_mut(); + let output_state: Option<&mut OutputState> + = find_output_mut(&mut collection, wl_output) + .map(|o| &mut o.pending); + match output_state { + Some(state) => { state.scale = factor; } + None => eprintln!("Wayland error: Got done on unknown output"), + }; + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_new() -> COutputs { + COutputs::new(Outputs { outputs: Vec::new() }) + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_free(outputs: COutputs) { + unsafe { outputs.unwrap() }; // gets dropped + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput) { + let collection = raw_collection.clone_ref(); + let mut collection = collection.borrow_mut(); + collection.outputs.push(Output { + output: output.clone(), + pending: OutputState::uninitialized(), + current: OutputState::uninitialized(), + }); + + unsafe { squeek_output_add_listener( + output, + &WlOutputListener { + geometry: outputs_handle_geometry, + mode: outputs_handle_mode, + done: outputs_handle_done, + scale: outputs_handle_scale, + } as *const WlOutputListener<COutputs>, + raw_collection, + )}; + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput { + let collection = raw_collection.clone_ref(); + let collection = collection.borrow(); + collection.outputs[0].output.clone() + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_get_perceptual_width( + raw_collection: COutputs, + wl_output: WlOutput, + ) -> i32 { + let collection = raw_collection.clone_ref(); + let collection = collection.borrow(); + + let output_state = find_output(&collection, wl_output) + .map(|o| &o.current); + match output_state { + Some(OutputState { + current_mode: Some(super::Mode { width, height } ), + transform: Some(transform), + scale, + }) => { + match transform { + Transform::Normal + | Transform::Rotated180 + | Transform::Flipped + | Transform::FlippedRotated180 => width / scale, + _ => height / scale, + } + }, + _ => { + eprintln!("Not enough info registered on output"); + 0 + }, + } + } + // TODO: handle unregistration + + fn find_output( + collection: &Outputs, + wl_output: WlOutput, + ) -> Option<&Output> { + collection.outputs + .iter() + .find_map(|o| + if o.output == wl_output { Some(o) } else { None } + ) + } + + fn find_output_mut( + collection: &mut Outputs, + wl_output: WlOutput, + ) -> Option<&mut Output> { + collection.outputs + .iter_mut() + .find_map(|o| + if o.output == wl_output { Some(o) } else { None } + ) + } +} + +#[derive(Clone)] +struct Mode { + width: i32, + height: i32, +} + +#[derive(Clone)] +pub struct OutputState { + current_mode: Option<Mode>, + transform: Option<c::Transform>, + scale: i32, +} + +impl OutputState { + fn uninitialized() -> OutputState { + OutputState { + current_mode: None, + transform: None, + scale: 1, + } + } +} + +pub struct Output { + output: c::WlOutput, + pending: OutputState, + current: OutputState, +} + +pub struct Outputs { + outputs: Vec<Output>, +} diff --git a/src/resources.rs b/src/resources.rs index df9a14ca96d82c92ed614063ccde25146df68192..e33ff4337e83b7d55236cf0b2d2d197575698290 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -2,9 +2,9 @@ * This could be done using GResource, but that would need additional work. */ - const KEYBOARDS: &[(*const str, *const str)] = &[ ("us", include_str!("../data/keyboards/us.yaml")), + ("us_wide", include_str!("../data/keyboards/us_wide.yaml")), ("de", include_str!("../data/keyboards/de.yaml")), ("el", include_str!("../data/keyboards/el.yaml")), ("es", include_str!("../data/keyboards/es.yaml")), diff --git a/src/server-context-service.c b/src/server-context-service.c index 75b69760826d0775fd38e633f5081f36f6c82335..137ae0cfcd1b344d94eb17f713b39d37265d453f 100644 --- a/src/server-context-service.c +++ b/src/server-context-service.c @@ -20,7 +20,8 @@ #include <gtk/gtk.h> #include <glib/gi18n.h> -#include "eek/eek-gtk.h" +#include "eek/eek.h" +#include "eek/eek-gtk-keyboard.h" #include "eek/layersurface.h" #include "wayland.h" @@ -38,9 +39,11 @@ typedef struct _ServerContextServiceClass ServerContextServiceClass; struct _ServerContextService { EekboardContextService parent; - GtkWidget *window; + PhoshLayerSurface *window; GtkWidget *widget; guint hiding; + guint last_requested_height; + enum squeek_arrangement_kind last_type; gdouble size_constraint_landscape[2]; gdouble size_constraint_portrait[2]; @@ -57,7 +60,7 @@ on_destroy (GtkWidget *widget, gpointer user_data) { ServerContextService *context = user_data; - g_assert (widget == context->window); + g_assert (widget == GTK_WIDGET(context->window)); context->window = NULL; context->widget = NULL; @@ -109,27 +112,84 @@ static void on_notify_unmap (GObject *object, ServerContextService *context) { + (void)object; g_object_set (context, "visible", FALSE, NULL); } -#define KEYBOARD_HEIGHT 210 +static uint32_t +calculate_height(int32_t width) +{ + uint32_t height = 180; + if (width < 360 && width > 0) { + height = ((unsigned)width * 7 / 12); // to match 360×210 + } else if (width < 540) { + height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition + } + return height; +} + +enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) { + (void)height; + if (width < 540) { + return ARRANGEMENT_KIND_BASE; + } + return ARRANGEMENT_KIND_WIDE; +} + +static void +on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context) +{ + gint width; + gint height; + g_object_get(G_OBJECT(surface), + "configured-width", &width, + "configured-height", &height, + NULL); + // check if the change would switch types + enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height); + if (context->last_type != new_type) { + context->last_type = new_type; + eekboard_context_service_update_layout(EEKBOARD_CONTEXT_SERVICE(context), context->last_type); + } + + guint desired_height = calculate_height(width); + guint configured_height = (guint)height; + // if height was already requested once but a different one was given + // (for the same set of surrounding properties), + // then it's probably not reasonable to ask for it again, + // as it's likely to create pointless loops + // of request->reject->request_again->... + if (desired_height != configured_height + && context->last_requested_height != desired_height) { + context->last_requested_height = desired_height; + phosh_layer_surface_set_size(surface, 0, + (gint)desired_height); + phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height); + phosh_layer_surface_wl_surface_commit (surface); + } +} + static void make_window (ServerContextService *context) { if (context->window) g_error("Window already present"); + struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs); + int32_t width = squeek_outputs_get_perceptual_width(squeek_wayland->outputs, output); + uint32_t height = calculate_height(width); + context->window = g_object_new ( PHOSH_TYPE_LAYER_SURFACE, "layer-shell", squeek_wayland->layer_shell, - "wl-output", g_ptr_array_index(squeek_wayland->outputs, 0), // TODO: select output as needed, - "height", KEYBOARD_HEIGHT, + "wl-output", output, + "height", height, "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, "layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP, "kbd-interactivity", FALSE, - "exclusive-zone", KEYBOARD_HEIGHT, + "exclusive-zone", height, "namespace", "osk", NULL ); @@ -138,6 +198,7 @@ make_window (ServerContextService *context) "signal::destroy", G_CALLBACK(on_destroy), context, "signal::map", G_CALLBACK(on_notify_map), context, "signal::unmap", G_CALLBACK(on_notify_unmap), context, + "signal::configured", G_CALLBACK(on_surface_configure), context, NULL); // The properties below are just to make hacking easier. @@ -145,7 +206,7 @@ make_window (ServerContextService *context) // and there's no space in the protocol for others. // Those may still be useful in the future, // or for hacks with regular windows. - gtk_widget_set_can_focus (context->window, FALSE); + gtk_widget_set_can_focus (GTK_WIDGET(context->window), FALSE); g_object_set (G_OBJECT(context->window), "accept_focus", FALSE, NULL); gtk_window_set_title (GTK_WINDOW(context->window), _("Squeekboard")); @@ -194,13 +255,13 @@ server_context_service_real_show_keyboard (EekboardContextService *_context) EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)-> show_keyboard (_context); - gtk_widget_show (context->window); + gtk_widget_show (GTK_WIDGET(context->window)); } static gboolean on_hide (ServerContextService *context) { - gtk_widget_hide (context->window); + gtk_widget_hide (GTK_WIDGET(context->window)); context->hiding = 0; return G_SOURCE_REMOVE; @@ -312,3 +373,8 @@ server_context_service_new () { return EEKBOARD_CONTEXT_SERVICE(g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL)); } + +enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService *service) +{ + return SERVER_CONTEXT_SERVICE(service)->last_type; +} diff --git a/src/server-context-service.h b/src/server-context-service.h index 6a20b62c43070749cd414254fec1d6a343e83e30..2929640a9a7f7aa5209f669a1b97e68fd915682b 100644 --- a/src/server-context-service.h +++ b/src/server-context-service.h @@ -19,6 +19,7 @@ #define SERVER_CONTEXT_SERVICE_H 1 #include "eekboard/eekboard-service.h" +#include "src/layout.h" G_BEGIN_DECLS @@ -33,6 +34,7 @@ G_BEGIN_DECLS typedef struct _ServerContextService ServerContextService; EekboardContextService *server_context_service_new (); +enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService*); G_END_DECLS #endif /* SERVER_CONTEXT_SERVICE_H */ diff --git a/src/server-main.c b/src/server-main.c index 96b37acdacb655df216efa0348b7597d60f3640d..56a2fd1f1af578f2bcf95881fc857e33ff22239d 100644 --- a/src/server-main.c +++ b/src/server-main.c @@ -28,6 +28,7 @@ #include "eekboard/eekboard-service.h" #include "eek/eek.h" #include "imservice.h" +#include "outputs.h" #include "server-context-service.h" #include "wayland.h" @@ -116,7 +117,7 @@ registry_handle_global (void *data, } else if (!strcmp (interface, "wl_output")) { struct wl_output *output = wl_registry_bind (registry, name, &wl_output_interface, 2); - g_ptr_array_add (instance->wayland.outputs, output); + squeek_outputs_register(instance->wayland.outputs, output); } else if (!strcmp(interface, "wl_seat")) { instance->wayland.seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); diff --git a/src/util.rs b/src/util.rs index 3a1b60200b5db42e88661f3cf9c3d9f2b0346257..baec594e8fbf76ba075dcdd4f979d5fe8726779d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -62,6 +62,10 @@ pub mod c { assert_eq!(as_str(&ptr::null()), Ok(None)) } } + + /// Marker trait for values that can be transferred to/received from C. + /// They must be either *const or *mut or repr(transparent). + pub trait COpaquePtr {} /// Wraps structures to pass them safely to/from C /// Since C doesn't respect borrowing rules, @@ -79,6 +83,9 @@ pub mod c { // which is a bit too complex for now. impl<T> Wrapped<T> { + pub fn new(value: T) -> Wrapped<T> { + Wrapped::wrap(Rc::new(RefCell::new(value))) + } pub fn wrap(state: Rc<RefCell<T>>) -> Wrapped<T> { Wrapped(Rc::into_raw(state)) } @@ -116,6 +123,8 @@ pub mod c { r.to_owned() } } + + impl<T> COpaquePtr for Wrapped<T> {} } pub trait CloneOwned { diff --git a/src/wayland.c b/src/wayland.c index cd10609e068be74f1b29e1d13a4a120b4778cbf5..96b037f4ebf21864a2b56f400102bc7794f3b79e 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -10,3 +10,8 @@ void eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t time, uint32_t key, uint32_t state) { zwp_virtual_keyboard_v1_key(zwp_virtual_keyboard_v1, time, key, state); } + +int squeek_output_add_listener(struct wl_output *wl_output, + const struct wl_output_listener *listener, void *data) { + return wl_output_add_listener(wl_output, listener, data); +} diff --git a/src/wayland.h b/src/wayland.h index 3907cc3570b6e6306887d12d68facb3157aa5e36..a411188d240e817315ea88a7269573139917fbc9 100644 --- a/src/wayland.h +++ b/src/wayland.h @@ -1,18 +1,19 @@ #ifndef WAYLAND_H #define WAYLAND_H +#include <gmodule.h> + #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "input-method-unstable-v2-client-protocol.h" -#include <gmodule.h> - +#include "outputs.h" struct squeek_wayland { struct zwlr_layer_shell_v1 *layer_shell; struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager; struct zwp_input_method_manager_v2 *input_method_manager; - GPtrArray *outputs; // *wl_output + struct squeek_outputs *outputs; struct wl_seat *seat; }; @@ -21,7 +22,7 @@ extern struct squeek_wayland *squeek_wayland; static inline void squeek_wayland_init(struct squeek_wayland *wayland) { - wayland->outputs = g_ptr_array_new(); + wayland->outputs = squeek_outputs_new(); } static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) { @@ -29,7 +30,7 @@ static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) { } static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) { - g_ptr_array_free(wayland->outputs, TRUE); + squeek_outputs_free(wayland->outputs); } #endif // WAYLAND_H diff --git a/tests/meson.build b/tests/meson.build index b30cefb86c7ba339f106d6f6f7ada24610b2bab7..2e3074b4f7abff57c7e57ab89f6895674f67d1e1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -47,7 +47,16 @@ endforeach # The layout test is in the examples directory # due to the way Cargo builds executables # and the need to call it manually -foreach layout : ['us', 'de', 'el', 'es', 'it', 'ja+kana', 'nb', 'number'] +foreach layout : [ + 'us', 'us_wide', + 'de', + 'el', + 'es', + 'it', + 'ja+kana', + 'nb', + 'number', +] test( 'test_layout_' + layout, cargo_script,