Compare commits
	
		
			14 Commits
		
	
	
		
			gerror_exi
			...
			sizing
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9ce2cf254b | |||
| 6d7360a230 | |||
| 33039e65cb | |||
| 4007754de9 | |||
| 6abaa36db8 | |||
| 710509a671 | |||
| 16d7fcae7c | |||
| e504154571 | |||
| b19938da01 | |||
| b409df15bb | |||
| 7dd2866b17 | |||
| f6fc6c83dc | |||
| fa5c7c63d9 | |||
| 1093e32325 | 
@ -45,7 +45,7 @@
 | 
			
		||||
typedef struct _EekGtkKeyboardPrivate
 | 
			
		||||
{
 | 
			
		||||
    EekRenderer *renderer; // owned, nullable
 | 
			
		||||
    EekboardContextService *eekboard_context; // unowned reference
 | 
			
		||||
    LayoutHolder *eekboard_context; // unowned reference
 | 
			
		||||
    struct submission *submission; // unowned reference
 | 
			
		||||
 | 
			
		||||
    struct squeek_layout_state *layout; // unowned
 | 
			
		||||
@ -124,9 +124,10 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
			
		||||
                (uint32_t)(allocation->width - allocation->x) * scale,
 | 
			
		||||
                (uint32_t)(allocation->height - allocation->y) * scale);
 | 
			
		||||
    if (priv->layout->arrangement != new_type) {
 | 
			
		||||
        priv->layout->arrangement = new_type;
 | 
			
		||||
        struct squeek_layout_state layout = *priv->layout;
 | 
			
		||||
        layout.arrangement = new_type;
 | 
			
		||||
 | 
			
		||||
        eekboard_context_service_use_layout(priv->eekboard_context, priv->layout);
 | 
			
		||||
        eek_layout_holder_use_layout(priv->eekboard_context, &layout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (priv->renderer)
 | 
			
		||||
@ -158,7 +159,7 @@ static void drag(EekGtkKeyboard *self,
 | 
			
		||||
    if (!priv->keyboard) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
    squeek_layout_drag(eek_layout_holder_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
                       priv->submission,
 | 
			
		||||
                       x, y, eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                       priv->eekboard_context, self);
 | 
			
		||||
@ -170,7 +171,7 @@ static void release(EekGtkKeyboard *self, guint32 time)
 | 
			
		||||
    if (!priv->keyboard) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
    squeek_layout_release(eek_layout_holder_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
                          priv->submission,
 | 
			
		||||
                          eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                          priv->eekboard_context, self);
 | 
			
		||||
@ -343,7 +344,7 @@ on_notify_keyboard (GObject              *object,
 | 
			
		||||
                    EekGtkKeyboard *self) {
 | 
			
		||||
    (void)spec;
 | 
			
		||||
    EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self);
 | 
			
		||||
    priv->keyboard = eekboard_context_service_get_keyboard(EEKBOARD_CONTEXT_SERVICE(object));
 | 
			
		||||
    priv->keyboard = eek_layout_holder_get_keyboard(LAYOUT_HOLDER(object));
 | 
			
		||||
    if (priv->renderer) {
 | 
			
		||||
        eek_renderer_free(priv->renderer);
 | 
			
		||||
    }
 | 
			
		||||
@ -356,7 +357,7 @@ on_notify_keyboard (GObject              *object,
 | 
			
		||||
 * Returns: a #GtkWidget
 | 
			
		||||
 */
 | 
			
		||||
GtkWidget *
 | 
			
		||||
eek_gtk_keyboard_new (EekboardContextService *eekservice,
 | 
			
		||||
eek_gtk_keyboard_new (LayoutHolder *eekservice,
 | 
			
		||||
                      struct submission *submission,
 | 
			
		||||
                      struct squeek_layout_state *layout)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ struct _EekGtkKeyboardClass
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType      eek_gtk_keyboard_get_type  (void) G_GNUC_CONST;
 | 
			
		||||
GtkWidget *eek_gtk_keyboard_new       (EekboardContextService *eekservice, struct submission *submission, struct squeek_layout_state *layout);
 | 
			
		||||
GtkWidget *eek_gtk_keyboard_new       (LayoutHolder *eekservice, struct submission *submission, struct squeek_layout_state *layout);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* EEK_GTK_KEYBOARD_H */
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
typedef struct _EekBounds EekBounds;
 | 
			
		||||
 | 
			
		||||
typedef struct _EekboardContextService EekboardContextService;
 | 
			
		||||
typedef struct _LayoutHolder LayoutHolder;
 | 
			
		||||
typedef struct _ServerContextService ServerContextService;
 | 
			
		||||
typedef struct _LevelKeyboard LevelKeyboard;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,28 +35,17 @@ enum {
 | 
			
		||||
    PROP_LAST
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    DESTROYED,
 | 
			
		||||
    LAST_SIGNAL
 | 
			
		||||
};
 | 
			
		||||
#define LAYOUT_HOLDER_GET_PRIVATE(obj)                       \
 | 
			
		||||
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolderPrivate))
 | 
			
		||||
 | 
			
		||||
static guint signals[LAST_SIGNAL] = { 0, };
 | 
			
		||||
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(obj)                       \
 | 
			
		||||
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
 | 
			
		||||
 | 
			
		||||
struct _EekboardContextServicePrivate {
 | 
			
		||||
struct _LayoutHolderPrivate {
 | 
			
		||||
    LevelKeyboard *keyboard; // currently used keyboard
 | 
			
		||||
    GSettings *settings; // Owned reference
 | 
			
		||||
 | 
			
		||||
    // Maybe TODO: it's used only for fetching layout type.
 | 
			
		||||
    // Maybe let UI push the type to this structure?
 | 
			
		||||
    ServerContextService *ui; // unowned reference
 | 
			
		||||
    /// Needed for keymap changes after keyboard updates
 | 
			
		||||
    struct submission *submission; // unowned
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
 | 
			
		||||
G_DEFINE_TYPE_WITH_PRIVATE (LayoutHolder, layout_holder, G_TYPE_OBJECT);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_set_property (GObject      *object,
 | 
			
		||||
@ -73,12 +62,12 @@ eekboard_context_service_set_property (GObject      *object,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_get_property (GObject    *object,
 | 
			
		||||
layout_holder_get_property (GObject    *object,
 | 
			
		||||
                                       guint       prop_id,
 | 
			
		||||
                                       GValue     *value,
 | 
			
		||||
                                       GParamSpec *pspec)
 | 
			
		||||
{
 | 
			
		||||
    EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
 | 
			
		||||
    LayoutHolder *context = LAYOUT_HOLDER(object);
 | 
			
		||||
 | 
			
		||||
    switch (prop_id) {
 | 
			
		||||
    case PROP_KEYBOARD:
 | 
			
		||||
@ -90,13 +79,6 @@ eekboard_context_service_get_property (GObject    *object,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_dispose (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
    G_OBJECT_CLASS (eekboard_context_service_parent_class)->
 | 
			
		||||
        dispose (object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
			
		||||
{
 | 
			
		||||
@ -116,7 +98,8 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state) {
 | 
			
		||||
eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *state) {
 | 
			
		||||
    *context->layout = *state;
 | 
			
		||||
    gchar *layout_name = state->overlay_name;
 | 
			
		||||
 | 
			
		||||
    if (layout_name == NULL) {
 | 
			
		||||
@ -160,73 +143,22 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void eekboard_context_service_update_settings_layout(EekboardContextService *context) {
 | 
			
		||||
    g_autofree gchar *keyboard_layout = NULL;
 | 
			
		||||
    g_autofree gchar *keyboard_type = NULL;
 | 
			
		||||
    settings_get_layout(context->priv->settings,
 | 
			
		||||
                        &keyboard_type, &keyboard_layout);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) {
 | 
			
		||||
        g_free(context->layout->overlay_name);
 | 
			
		||||
        context->layout->overlay_name = NULL;
 | 
			
		||||
        if (keyboard_layout) {
 | 
			
		||||
            g_free(context->layout->layout_name);
 | 
			
		||||
            context->layout->layout_name = g_strdup(keyboard_layout);
 | 
			
		||||
        }
 | 
			
		||||
        // This must actually update the UI.
 | 
			
		||||
        eekboard_context_service_use_layout(context, context->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
settings_handle_layout_changed(GSettings *s,
 | 
			
		||||
                               gpointer keys, gint n_keys,
 | 
			
		||||
                               gpointer user_data) {
 | 
			
		||||
    (void)s;
 | 
			
		||||
    (void)keys;
 | 
			
		||||
    (void)n_keys;
 | 
			
		||||
    EekboardContextService *context = user_data;
 | 
			
		||||
    eekboard_context_service_update_settings_layout(context);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
static void
 | 
			
		||||
layout_holder_init (LayoutHolder *self) {
 | 
			
		||||
    self->priv = LAYOUT_HOLDER_GET_PRIVATE(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_constructed (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
    (void)object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_class_init (EekboardContextServiceClass *klass)
 | 
			
		||||
layout_holder_class_init (LayoutHolderClass *klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | 
			
		||||
    GParamSpec *pspec;
 | 
			
		||||
 | 
			
		||||
    gobject_class->constructed = eekboard_context_service_constructed;
 | 
			
		||||
    gobject_class->set_property = eekboard_context_service_set_property;
 | 
			
		||||
    gobject_class->get_property = eekboard_context_service_get_property;
 | 
			
		||||
    gobject_class->dispose = eekboard_context_service_dispose;
 | 
			
		||||
    /**
 | 
			
		||||
     * EekboardContextService::destroyed:
 | 
			
		||||
     * @context: an #EekboardContextService
 | 
			
		||||
     *
 | 
			
		||||
     * Emitted when @context is destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    signals[DESTROYED] =
 | 
			
		||||
        g_signal_new (I_("destroyed"),
 | 
			
		||||
                      G_TYPE_FROM_CLASS(gobject_class),
 | 
			
		||||
                      G_SIGNAL_RUN_LAST,
 | 
			
		||||
                      G_STRUCT_OFFSET(EekboardContextServiceClass, destroyed),
 | 
			
		||||
                      NULL,
 | 
			
		||||
                      NULL,
 | 
			
		||||
                      g_cclosure_marshal_VOID__VOID,
 | 
			
		||||
                      G_TYPE_NONE,
 | 
			
		||||
                      0);
 | 
			
		||||
    gobject_class->get_property = layout_holder_get_property;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * EekboardContextService:keyboard:
 | 
			
		||||
     *
 | 
			
		||||
     * An #EekKeyboard currently active in this context.
 | 
			
		||||
     * An #LevelKeyboard currently active in this context.
 | 
			
		||||
     */
 | 
			
		||||
    pspec = g_param_spec_pointer("keyboard",
 | 
			
		||||
                                 "Keyboard",
 | 
			
		||||
@ -237,10 +169,91 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
 | 
			
		||||
                                     pspec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
eekboard_context_service_init (EekboardContextService *self)
 | 
			
		||||
/**
 | 
			
		||||
 * Get keyboard currently active in @context.
 | 
			
		||||
 * Returns: (transfer none): a LevelKeyboard
 | 
			
		||||
 */
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
eek_layout_holder_get_keyboard (LayoutHolder *context)
 | 
			
		||||
{
 | 
			
		||||
    self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self);
 | 
			
		||||
    return context->priv->keyboard;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_hint_purpose(LayoutHolder *context,
 | 
			
		||||
                                               uint32_t hint, uint32_t purpose)
 | 
			
		||||
{
 | 
			
		||||
    if (context->layout->hint != hint || context->layout->purpose != purpose) {
 | 
			
		||||
        context->layout->hint = hint;
 | 
			
		||||
        context->layout->purpose = purpose;
 | 
			
		||||
        eek_layout_holder_use_layout(context, context->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_set_overlay(LayoutHolder *context, const char* name) {
 | 
			
		||||
    if (g_strcmp0(context->layout->overlay_name, name)) {
 | 
			
		||||
        g_free(context->layout->overlay_name);
 | 
			
		||||
        context->layout->overlay_name = g_strdup(name);
 | 
			
		||||
        eek_layout_holder_use_layout(context, context->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
eekboard_context_service_get_overlay(LayoutHolder *context) {
 | 
			
		||||
    return context->layout->overlay_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayoutHolder *eek_layout_holder_new(struct squeek_layout_state *state)
 | 
			
		||||
{
 | 
			
		||||
    LayoutHolder *context = g_object_new (EEKBOARD_TYPE_LAYOUT_HOLDER, NULL);
 | 
			
		||||
    context->layout = state;
 | 
			
		||||
    eek_layout_holder_use_layout(context, context->layout);
 | 
			
		||||
    return context;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eek_layout_holder_set_submission(LayoutHolder *context, struct submission *submission) {
 | 
			
		||||
    context->priv->submission = submission;
 | 
			
		||||
    if (context->priv->submission) {
 | 
			
		||||
        submission_set_keyboard(context->priv->submission, context->priv->keyboard);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void settings_update_layout(struct gsettings_tracker *self) {
 | 
			
		||||
    // The layout in the param must be the same layout as held by context.
 | 
			
		||||
    g_autofree gchar *keyboard_layout = NULL;
 | 
			
		||||
    g_autofree gchar *keyboard_type = NULL;
 | 
			
		||||
    settings_get_layout(self->gsettings,
 | 
			
		||||
                        &keyboard_type, &keyboard_layout);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0(self->layout->layout_name, keyboard_layout) != 0 || self->layout->overlay_name) {
 | 
			
		||||
        g_free(self->layout->overlay_name);
 | 
			
		||||
        self->layout->overlay_name = NULL;
 | 
			
		||||
        if (keyboard_layout) {
 | 
			
		||||
            g_free(self->layout->layout_name);
 | 
			
		||||
            self->layout->layout_name = g_strdup(keyboard_layout);
 | 
			
		||||
        }
 | 
			
		||||
        // This must actually update the UI.
 | 
			
		||||
        eek_layout_holder_use_layout(self->context, self->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
handle_layout_changed(GSettings *s,
 | 
			
		||||
                               gpointer keys, gint n_keys,
 | 
			
		||||
                               gpointer user_data) {
 | 
			
		||||
    (void)s;
 | 
			
		||||
    (void)keys;
 | 
			
		||||
    (void)n_keys;
 | 
			
		||||
    struct gsettings_tracker *self = user_data;
 | 
			
		||||
    settings_update_layout(self);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eek_gsettings_tracker_init(struct gsettings_tracker *tracker, LayoutHolder *context, struct squeek_layout_state *layout)
 | 
			
		||||
{
 | 
			
		||||
    tracker->layout = layout;
 | 
			
		||||
    tracker->context = context;
 | 
			
		||||
 | 
			
		||||
    const char *schema_name = "org.gnome.desktop.input-sources";
 | 
			
		||||
    GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
 | 
			
		||||
    if (ssrc) {
 | 
			
		||||
@ -250,10 +263,10 @@ eekboard_context_service_init (EekboardContextService *self)
 | 
			
		||||
        if (schema) {
 | 
			
		||||
            // Not referencing the found schema directly,
 | 
			
		||||
            // because it's not clear how...
 | 
			
		||||
            self->priv->settings = g_settings_new (schema_name);
 | 
			
		||||
            gulong conn_id = g_signal_connect(self->priv->settings, "change-event",
 | 
			
		||||
                                              G_CALLBACK(settings_handle_layout_changed),
 | 
			
		||||
                                              self);
 | 
			
		||||
            tracker->gsettings = g_settings_new (schema_name);
 | 
			
		||||
            gulong conn_id = g_signal_connect(tracker->gsettings, "change-event",
 | 
			
		||||
                                              G_CALLBACK(handle_layout_changed),
 | 
			
		||||
                                              tracker);
 | 
			
		||||
            if (conn_id == 0) {
 | 
			
		||||
                g_warning ("Could not connect to gsettings updates, "
 | 
			
		||||
                           "automatic layout changing unavailable");
 | 
			
		||||
@ -265,74 +278,6 @@ eekboard_context_service_init (EekboardContextService *self)
 | 
			
		||||
    } else {
 | 
			
		||||
        g_warning("No gsettings schemas installed. Layout switching unavailable.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eekboard_context_service_destroy:
 | 
			
		||||
 * @context: an #EekboardContextService
 | 
			
		||||
 *
 | 
			
		||||
 * Destroy @context.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_destroy (EekboardContextService *context)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
 | 
			
		||||
    g_signal_emit (context, signals[DESTROYED], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eekboard_context_service_get_keyboard:
 | 
			
		||||
 * @context: an #EekboardContextService
 | 
			
		||||
 *
 | 
			
		||||
 * Get keyboard currently active in @context.
 | 
			
		||||
 * Returns: (transfer none): an #EekKeyboard
 | 
			
		||||
 */
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
eekboard_context_service_get_keyboard (EekboardContextService *context)
 | 
			
		||||
{
 | 
			
		||||
    return context->priv->keyboard;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
 | 
			
		||||
                                               uint32_t hint, uint32_t purpose)
 | 
			
		||||
{
 | 
			
		||||
    if (context->layout->hint != hint || context->layout->purpose != purpose) {
 | 
			
		||||
        context->layout->hint = hint;
 | 
			
		||||
        context->layout->purpose = purpose;
 | 
			
		||||
        eekboard_context_service_use_layout(context, context->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
 | 
			
		||||
    if (g_strcmp0(context->layout->overlay_name, name)) {
 | 
			
		||||
        g_free(context->layout->overlay_name);
 | 
			
		||||
        context->layout->overlay_name = g_strdup(name);
 | 
			
		||||
        eekboard_context_service_use_layout(context, context->layout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
eekboard_context_service_get_overlay(EekboardContextService *context) {
 | 
			
		||||
    return context->layout->overlay_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state)
 | 
			
		||||
{
 | 
			
		||||
    EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
 | 
			
		||||
    context->layout = state;
 | 
			
		||||
    eekboard_context_service_update_settings_layout(context);
 | 
			
		||||
    eekboard_context_service_use_layout(context, context->layout);
 | 
			
		||||
    return context;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
 | 
			
		||||
    context->priv->submission = submission;
 | 
			
		||||
    if (context->priv->submission) {
 | 
			
		||||
        submission_set_keyboard(context->priv->submission, context->priv->keyboard);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
 | 
			
		||||
    context->priv->ui = ui;
 | 
			
		||||
    settings_update_layout(tracker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,71 +30,60 @@
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE_PATH "/org/fedorahosted/Eekboard/Context_%d"
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context"
 | 
			
		||||
 | 
			
		||||
#define EEKBOARD_TYPE_CONTEXT_SERVICE (eekboard_context_service_get_type())
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextService))
 | 
			
		||||
#define EEKBOARD_TYPE_LAYOUT_HOLDER (layout_holder_get_type())
 | 
			
		||||
#define LAYOUT_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolder))
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
 | 
			
		||||
#define EEKBOARD_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE))
 | 
			
		||||
#define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE))
 | 
			
		||||
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
 | 
			
		||||
typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
 | 
			
		||||
typedef struct _LayoutHolderClass LayoutHolderClass;
 | 
			
		||||
typedef struct _LayoutHolderPrivate LayoutHolderPrivate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * EekboardContextService:
 | 
			
		||||
 *
 | 
			
		||||
 * Handles layout state, gsettings, and virtual-keyboard.
 | 
			
		||||
 * Handles layout state, and virtual-keyboard.
 | 
			
		||||
 *
 | 
			
		||||
 * TODO: Restrict to managing keyboard layouts, and maybe button repeats,
 | 
			
		||||
 * and the virtual keyboard protocol.
 | 
			
		||||
 *
 | 
			
		||||
 * The #EekboardContextService structure contains only private data
 | 
			
		||||
 * and should only be accessed using the provided API.
 | 
			
		||||
 */
 | 
			
		||||
struct _EekboardContextService {
 | 
			
		||||
struct _LayoutHolder {
 | 
			
		||||
    GObject parent;
 | 
			
		||||
    EekboardContextServicePrivate *priv;
 | 
			
		||||
    LayoutHolderPrivate *priv;
 | 
			
		||||
    struct squeek_layout_state *layout; // Unowned
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * EekboardContextServiceClass:
 | 
			
		||||
 * @create_keyboard: virtual function for create a keyboard from string
 | 
			
		||||
 * @enabled: class handler for #EekboardContextService::enabled signal
 | 
			
		||||
 * @disabled: class handler for #EekboardContextService::disabled signal
 | 
			
		||||
 */
 | 
			
		||||
struct _EekboardContextServiceClass {
 | 
			
		||||
struct _LayoutHolderClass {
 | 
			
		||||
    /*< private >*/
 | 
			
		||||
    GObjectClass parent_class;
 | 
			
		||||
 | 
			
		||||
    /*< public >*/
 | 
			
		||||
    /* signals */
 | 
			
		||||
    void         (*destroyed)          (EekboardContextService *self);
 | 
			
		||||
 | 
			
		||||
    /*< private >*/
 | 
			
		||||
    /* padding */
 | 
			
		||||
    gpointer pdummy[24];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType         eekboard_context_service_get_type
 | 
			
		||||
                                              (void) G_GNUC_CONST;
 | 
			
		||||
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state);
 | 
			
		||||
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
 | 
			
		||||
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
 | 
			
		||||
void          eekboard_context_service_destroy (EekboardContextService *context);
 | 
			
		||||
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
 | 
			
		||||
GType         layout_holder_get_type(void) G_GNUC_CONST;
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_keymap(EekboardContextService *context,
 | 
			
		||||
/// Handles gsettings os-level keyboard layout switches.
 | 
			
		||||
struct gsettings_tracker {
 | 
			
		||||
    GSettings *gsettings; // Owned reference
 | 
			
		||||
    LayoutHolder *context; // Unowned
 | 
			
		||||
    struct squeek_layout_state *layout; // Unowned
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void eek_gsettings_tracker_init(struct gsettings_tracker* tracker, LayoutHolder *context, struct squeek_layout_state *layout);
 | 
			
		||||
 | 
			
		||||
LayoutHolder *eek_layout_holder_new(struct squeek_layout_state *state);
 | 
			
		||||
void eek_layout_holder_set_submission(LayoutHolder *context, struct submission *submission);
 | 
			
		||||
LevelKeyboard *eek_layout_holder_get_keyboard(LayoutHolder *context);
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_keymap(LayoutHolder *context,
 | 
			
		||||
                                         const LevelKeyboard *keyboard);
 | 
			
		||||
 | 
			
		||||
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
 | 
			
		||||
void eekboard_context_service_set_hint_purpose(LayoutHolder *context,
 | 
			
		||||
                                               uint32_t hint,
 | 
			
		||||
                                               uint32_t purpose);
 | 
			
		||||
void
 | 
			
		||||
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout);
 | 
			
		||||
eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *layout);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* EEKBOARD_CONTEXT_SERVICE_H */
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ static const struct zwp_input_method_v2_listener input_method_listener = {
 | 
			
		||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
                                  struct zwp_virtual_keyboard_manager_v1 *vkmanager,
 | 
			
		||||
                                  struct wl_seat *seat,
 | 
			
		||||
                                  EekboardContextService *state) {
 | 
			
		||||
                                  LayoutHolder *state) {
 | 
			
		||||
    struct zwp_input_method_v2 *im = NULL;
 | 
			
		||||
    if (immanager) {
 | 
			
		||||
        im = zwp_input_method_manager_v2_get_input_method(immanager, seat);
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,7 @@ void squeek_layout_release(struct squeek_layout *layout,
 | 
			
		||||
                           struct submission *submission,
 | 
			
		||||
                           struct transformation widget_to_layout,
 | 
			
		||||
                           uint32_t timestamp,
 | 
			
		||||
                           EekboardContextService *manager,
 | 
			
		||||
                           LayoutHolder *manager,
 | 
			
		||||
                           EekGtkKeyboard *ui_keyboard);
 | 
			
		||||
void squeek_layout_release_all_only(struct squeek_layout *layout,
 | 
			
		||||
                                    struct submission *submission,
 | 
			
		||||
@ -61,7 +61,7 @@ void squeek_layout_drag(struct squeek_layout *layout,
 | 
			
		||||
                        struct submission *submission,
 | 
			
		||||
                        double x_widget, double y_widget,
 | 
			
		||||
                        struct transformation widget_to_layout,
 | 
			
		||||
                        uint32_t timestamp, EekboardContextService *manager,
 | 
			
		||||
                        uint32_t timestamp, LayoutHolder *manager,
 | 
			
		||||
                        EekGtkKeyboard *ui_keyboard);
 | 
			
		||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t     *cr, struct submission *submission);
 | 
			
		||||
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t     *cr);
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ sources = [
 | 
			
		||||
  'dbus.c',
 | 
			
		||||
  'imservice.c',
 | 
			
		||||
  'server-context-service.c',
 | 
			
		||||
  'ui_manager.c',
 | 
			
		||||
  'wayland.c',
 | 
			
		||||
  '../eek/eek.c',
 | 
			
		||||
  '../eek/eek-element.c',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										152
									
								
								src/outputs.rs
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								src/outputs.rs
									
									
									
									
									
								
							@ -1,5 +1,10 @@
 | 
			
		||||
/* Copyright (C) 2019-2020 Purism SPC
 | 
			
		||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! Managing Wayland outputs */
 | 
			
		||||
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::vec::Vec;
 | 
			
		||||
use ::logging;
 | 
			
		||||
 | 
			
		||||
@ -17,7 +22,7 @@ pub mod c {
 | 
			
		||||
    // Defined in C
 | 
			
		||||
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, PartialEq, Copy)]
 | 
			
		||||
    #[derive(Clone, PartialEq, Copy, Hash)]
 | 
			
		||||
    pub struct WlOutput(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
@ -63,7 +68,7 @@ pub mod c {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Map to `wl_output.transform` values
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    #[derive(Clone, PartialEq)]
 | 
			
		||||
    pub enum Transform {
 | 
			
		||||
        Normal = 0,
 | 
			
		||||
        Rotated90 = 1,
 | 
			
		||||
@ -103,7 +108,7 @@ pub mod c {
 | 
			
		||||
        ) -> i32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    type COutputs = ::util::c::Wrapped<Outputs>;
 | 
			
		||||
    pub type COutputs = ::util::c::Wrapped<Outputs>;
 | 
			
		||||
 | 
			
		||||
    /// A stable reference to an output.
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
@ -121,6 +126,10 @@ pub mod c {
 | 
			
		||||
            let outputs = outputs.borrow();
 | 
			
		||||
            find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn get_id(&self) -> OutputId {
 | 
			
		||||
            OutputId { wl_output: self.wl_output }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Defined in Rust
 | 
			
		||||
@ -129,7 +138,7 @@ pub mod c {
 | 
			
		||||
        outputs: COutputs,
 | 
			
		||||
        wl_output: WlOutput,
 | 
			
		||||
        _x: i32, _y: i32,
 | 
			
		||||
        _phys_width: i32, _phys_height: i32,
 | 
			
		||||
        phys_width: i32, phys_height: i32,
 | 
			
		||||
        _subpixel: i32,
 | 
			
		||||
        _make: *const c_char, _model: *const c_char,
 | 
			
		||||
        transform: i32,
 | 
			
		||||
@ -145,8 +154,23 @@ pub mod c {
 | 
			
		||||
        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) },
 | 
			
		||||
            Some(state) => {
 | 
			
		||||
                state.transform = Some(transform);
 | 
			
		||||
                state.phys_size = {
 | 
			
		||||
                    if (phys_width > 0) & (phys_height > 0) {
 | 
			
		||||
                        Some(SizeMM { width: phys_width, height: phys_height })
 | 
			
		||||
                    } else {
 | 
			
		||||
                        log_print!(
 | 
			
		||||
                            logging::Level::Surprise,
 | 
			
		||||
                            "Impossible physical dimensions: {}mm × {}mm",
 | 
			
		||||
                            phys_width, phys_height,
 | 
			
		||||
                        );
 | 
			
		||||
                        None
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            None => log_print!(
 | 
			
		||||
                logging::Level::Warning,
 | 
			
		||||
                "Got geometry on unknown output",
 | 
			
		||||
@ -187,19 +211,27 @@ pub mod c {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern fn outputs_handle_done(
 | 
			
		||||
        outputs: COutputs,
 | 
			
		||||
        outputs_raw: 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 => log_print!(
 | 
			
		||||
                logging::Level::Warning,
 | 
			
		||||
                "Got done on unknown output",
 | 
			
		||||
            ),
 | 
			
		||||
        };
 | 
			
		||||
        let outputs = outputs_raw.clone_ref();
 | 
			
		||||
        {
 | 
			
		||||
            let mut collection = RefCell::borrow_mut(&outputs);
 | 
			
		||||
            let output = find_output_mut(&mut collection, wl_output);
 | 
			
		||||
            match output {
 | 
			
		||||
                Some(output) => { output.current = output.pending.clone(); }
 | 
			
		||||
                None => log_print!(
 | 
			
		||||
                    logging::Level::Warning,
 | 
			
		||||
                    "Got done on unknown output",
 | 
			
		||||
                ),
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        let collection = RefCell::borrow(&outputs);
 | 
			
		||||
        if let Some(ref cb) = &collection.update_cb {
 | 
			
		||||
            let mut cb = RefCell::borrow_mut(cb);
 | 
			
		||||
            let cb = Box::as_mut(&mut cb);
 | 
			
		||||
            cb(OutputHandle { wl_output, outputs: outputs_raw });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern fn outputs_handle_scale(
 | 
			
		||||
@ -224,7 +256,10 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_outputs_new() -> COutputs {
 | 
			
		||||
        COutputs::new(Outputs { outputs: Vec::new() })
 | 
			
		||||
        COutputs::new(Outputs {
 | 
			
		||||
            outputs: Vec::new(),
 | 
			
		||||
            update_cb: None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
@ -293,22 +328,29 @@ pub mod c {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic size
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Size {
 | 
			
		||||
    pub width: u32,
 | 
			
		||||
    pub height: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq)]
 | 
			
		||||
pub struct SizeMM {
 | 
			
		||||
    pub width: i32,
 | 
			
		||||
    pub height: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// wl_output mode
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[derive(Clone, PartialEq)]
 | 
			
		||||
struct Mode {
 | 
			
		||||
    width: i32,
 | 
			
		||||
    height: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[derive(Clone, PartialEq)]
 | 
			
		||||
pub struct OutputState {
 | 
			
		||||
    current_mode: Option<Mode>,
 | 
			
		||||
    phys_size: Option<SizeMM>,
 | 
			
		||||
    transform: Option<c::Transform>,
 | 
			
		||||
    pub scale: i32,
 | 
			
		||||
}
 | 
			
		||||
@ -323,6 +365,7 @@ impl OutputState {
 | 
			
		||||
    fn uninitialized() -> OutputState {
 | 
			
		||||
        OutputState {
 | 
			
		||||
            current_mode: None,
 | 
			
		||||
            phys_size: None,
 | 
			
		||||
            transform: None,
 | 
			
		||||
            scale: 1,
 | 
			
		||||
        }
 | 
			
		||||
@ -334,6 +377,35 @@ impl OutputState {
 | 
			
		||||
            OutputState {
 | 
			
		||||
                current_mode: Some(Mode { width, height } ),
 | 
			
		||||
                transform: Some(transform),
 | 
			
		||||
                phys_size: _,
 | 
			
		||||
                scale: _,
 | 
			
		||||
            } => Some(
 | 
			
		||||
                match transform {
 | 
			
		||||
                    Transform::Normal
 | 
			
		||||
                    | Transform::Rotated180
 | 
			
		||||
                    | Transform::Flipped
 | 
			
		||||
                    | Transform::FlippedRotated180 => Size {
 | 
			
		||||
                        width: *width as u32,
 | 
			
		||||
                        height: *height as u32,
 | 
			
		||||
                    },
 | 
			
		||||
                    _ => Size {
 | 
			
		||||
                        width: *height as u32,
 | 
			
		||||
                        height: *width as u32,
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Returns transformed dimensions
 | 
			
		||||
    pub fn get_phys_size(&self) -> Option<Size> {
 | 
			
		||||
        use self::c::Transform;
 | 
			
		||||
        match self {
 | 
			
		||||
            OutputState {
 | 
			
		||||
                current_mode: _,
 | 
			
		||||
                transform: Some(transform),
 | 
			
		||||
                phys_size: Some(SizeMM { width, height }),
 | 
			
		||||
                scale: _,
 | 
			
		||||
            } => Some(
 | 
			
		||||
                match transform {
 | 
			
		||||
@ -355,12 +427,52 @@ impl OutputState {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A comparable ID of an output
 | 
			
		||||
#[derive(Clone, PartialEq, Hash)]
 | 
			
		||||
pub struct OutputId {
 | 
			
		||||
    // WlOutput is a unique pointer, so it will not repeat
 | 
			
		||||
    // even if there are multiple output managers.
 | 
			
		||||
    wl_output: c::WlOutput,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Output {
 | 
			
		||||
    output: c::WlOutput,
 | 
			
		||||
    pending: OutputState,
 | 
			
		||||
    current: OutputState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The manager of all outputs.
 | 
			
		||||
// This is the target of several callbacks,
 | 
			
		||||
// so it should only be used with a stable place in memory, like `Rc<RefCell>`.
 | 
			
		||||
// It should not be instantiated externally or copied,
 | 
			
		||||
// or it will not receive those callbacks and be somewhat of an empty shell.
 | 
			
		||||
// It should be safe to use as long as the fields are not `pub`,
 | 
			
		||||
// and there's no `Clone`, and this module's API only ever gives out
 | 
			
		||||
// references wrapped in `Rc<RefCell>`.
 | 
			
		||||
// For perfectness, it would only ever give out immutable opaque references,
 | 
			
		||||
// but that's not practical at the moment.
 | 
			
		||||
// `mem::swap` could replace the value inside,
 | 
			
		||||
// but as long as the swap is atomic,
 | 
			
		||||
// that should not cause an inconsistent state.
 | 
			
		||||
pub struct Outputs {
 | 
			
		||||
    outputs: Vec<Output>,
 | 
			
		||||
    // The RefCell is here to let the function be called
 | 
			
		||||
    // while holding only a read-reference to `Outputs`.
 | 
			
		||||
    // Otherwise anything trying to get useful data from OutputHandle
 | 
			
		||||
    // will fail to acquire reference to Outputs.
 | 
			
		||||
    // TODO: Maybe pass only current state along with Outputs and Output hash.
 | 
			
		||||
    // The only reason a full OutputHandle is here
 | 
			
		||||
    // is to be able to track the right Output.
 | 
			
		||||
    update_cb: Option<RefCell<Box<dyn FnMut(c::OutputHandle)>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Outputs {
 | 
			
		||||
    /// The function will get called whenever
 | 
			
		||||
    /// any output changes or is removed or created.
 | 
			
		||||
    /// If output handle doesn't return state, the output just went down.
 | 
			
		||||
    /// It cannot modify anything in Outputs.
 | 
			
		||||
    // FIXME: handle output destruction
 | 
			
		||||
    pub fn set_update_cb(&mut self, callback: Box<dyn FnMut(c::OutputHandle)>) {
 | 
			
		||||
        self.update_cb = Some(RefCell::new(callback));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ typedef struct _ServerContextServiceClass ServerContextServiceClass;
 | 
			
		||||
struct _ServerContextService {
 | 
			
		||||
    GObject parent;
 | 
			
		||||
 | 
			
		||||
    EekboardContextService *state; // unowned
 | 
			
		||||
    LayoutHolder *state; // unowned
 | 
			
		||||
    /// Needed for instantiating the widget
 | 
			
		||||
    struct submission *submission; // unowned
 | 
			
		||||
    struct squeek_layout_state *layout;
 | 
			
		||||
@ -139,6 +139,7 @@ make_window (ServerContextService *context)
 | 
			
		||||
        NULL
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    squeek_uiman_set_surface(context->manager, context->window);
 | 
			
		||||
    g_object_connect (context->window,
 | 
			
		||||
                      "signal::destroy", G_CALLBACK(on_destroy), context,
 | 
			
		||||
                      "signal::map", G_CALLBACK(on_notify_map), context,
 | 
			
		||||
@ -311,7 +312,7 @@ server_context_service_init (ServerContextService *state) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ServerContextService *
 | 
			
		||||
server_context_service_new (EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman)
 | 
			
		||||
server_context_service_new (LayoutHolder *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman)
 | 
			
		||||
{
 | 
			
		||||
    ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
 | 
			
		||||
    ui->submission = submission;
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ typedef struct _ServerContextService ServerContextService;
 | 
			
		||||
GType         server_context_service_get_type
 | 
			
		||||
                                              (void) G_GNUC_CONST;
 | 
			
		||||
 | 
			
		||||
ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman);
 | 
			
		||||
ServerContextService *server_context_service_new(LayoutHolder *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman);
 | 
			
		||||
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
			
		||||
void server_context_service_show_keyboard (ServerContextService *context);
 | 
			
		||||
void server_context_service_hide_keyboard (ServerContextService *context);
 | 
			
		||||
 | 
			
		||||
@ -42,11 +42,12 @@
 | 
			
		||||
struct squeekboard {
 | 
			
		||||
    struct squeek_wayland wayland; // Just hooks.
 | 
			
		||||
    DBusHandler *dbus_handler; // Controls visibility of the OSK.
 | 
			
		||||
    EekboardContextService *settings_context; // Gsettings hooks.
 | 
			
		||||
    LayoutHolder *layout_holder; // Currently used layout & keyboard.
 | 
			
		||||
    ServerContextService *ui_context; // mess, includes the entire UI
 | 
			
		||||
    struct submission *submission; // Wayland text input handling.
 | 
			
		||||
    struct squeek_layout_state layout_choice; // Currently wanted layout.
 | 
			
		||||
    struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice
 | 
			
		||||
    struct gsettings_tracker gsettings_tracker; // Gsettings handling.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -203,10 +204,10 @@ main (int argc, char **argv)
 | 
			
		||||
        g_warning("Wayland input method interface not available");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    instance.ui_manager = squeek_uiman_new();
 | 
			
		||||
 | 
			
		||||
    instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
 | 
			
		||||
    instance.ui_manager = squeek_uiman_new(instance.wayland.outputs);
 | 
			
		||||
 | 
			
		||||
    instance.layout_holder = eek_layout_holder_new(&instance.layout_choice);
 | 
			
		||||
    eek_gsettings_tracker_init(&instance.gsettings_tracker, instance.layout_holder, &instance.layout_choice);
 | 
			
		||||
    // set up dbus
 | 
			
		||||
 | 
			
		||||
    // TODO: make dbus errors non-always-fatal
 | 
			
		||||
@ -279,12 +280,12 @@ main (int argc, char **argv)
 | 
			
		||||
    instance.submission = get_submission(instance.wayland.input_method_manager,
 | 
			
		||||
                                         instance.wayland.virtual_keyboard_manager,
 | 
			
		||||
                                         instance.wayland.seat,
 | 
			
		||||
                                         instance.settings_context);
 | 
			
		||||
                                         instance.layout_holder);
 | 
			
		||||
 | 
			
		||||
    eekboard_context_service_set_submission(instance.settings_context, instance.submission);
 | 
			
		||||
    eek_layout_holder_set_submission(instance.layout_holder, instance.submission);
 | 
			
		||||
 | 
			
		||||
    ServerContextService *ui_context = server_context_service_new(
 | 
			
		||||
                instance.settings_context,
 | 
			
		||||
                instance.layout_holder,
 | 
			
		||||
                instance.submission,
 | 
			
		||||
                &instance.layout_choice,
 | 
			
		||||
                instance.ui_manager);
 | 
			
		||||
@ -299,7 +300,6 @@ main (int argc, char **argv)
 | 
			
		||||
    if (instance.dbus_handler) {
 | 
			
		||||
        dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
 | 
			
		||||
    }
 | 
			
		||||
    eekboard_context_service_set_ui(instance.settings_context, instance.ui_context);
 | 
			
		||||
 | 
			
		||||
    session_register();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,10 +10,10 @@ struct submission;
 | 
			
		||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
                                  struct zwp_virtual_keyboard_manager_v1 *vkmanager,
 | 
			
		||||
                                  struct wl_seat *seat,
 | 
			
		||||
                                  EekboardContextService *state);
 | 
			
		||||
                                  LayoutHolder *state);
 | 
			
		||||
 | 
			
		||||
// Defined in Rust
 | 
			
		||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
 | 
			
		||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, LayoutHolder *state);
 | 
			
		||||
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
 | 
			
		||||
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/ui_manager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/ui_manager.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
#include "eek/layersurface.h"
 | 
			
		||||
 | 
			
		||||
void squeek_manager_set_surface_height(PhoshLayerSurface *surface, uint32_t 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);
 | 
			
		||||
}
 | 
			
		||||
@ -2,13 +2,14 @@
 | 
			
		||||
#define UI_MANAGER__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include "eek/layersurface.h"
 | 
			
		||||
#include "outputs.h"
 | 
			
		||||
 | 
			
		||||
struct ui_manager;
 | 
			
		||||
 | 
			
		||||
struct ui_manager *squeek_uiman_new();
 | 
			
		||||
struct ui_manager *squeek_uiman_new(struct squeek_outputs *outputs);
 | 
			
		||||
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
 | 
			
		||||
void squeek_uiman_set_surface(struct ui_manager *uiman, PhoshLayerSurface *surface);
 | 
			
		||||
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -7,17 +7,48 @@
 | 
			
		||||
 * Coordinates this based on information collated from all possible sources.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::cmp::min;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use ::logging;
 | 
			
		||||
use ::outputs::{ OutputId, Outputs, OutputState};
 | 
			
		||||
use ::outputs::c::OutputHandle;
 | 
			
		||||
 | 
			
		||||
// Traits
 | 
			
		||||
use ::logging::Warn;
 | 
			
		||||
 | 
			
		||||
mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use ::outputs::c::COutputs;
 | 
			
		||||
    use ::util::c::Wrapped;
 | 
			
		||||
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct PhoshLayerSurface(*const c_void);
 | 
			
		||||
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // Rustc wrongly assumes
 | 
			
		||||
        // that COutputs allows C direct access to the underlying RefCell.
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn squeek_manager_set_surface_height(
 | 
			
		||||
            surface: PhoshLayerSurface,
 | 
			
		||||
            height: u32,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_uiman_new() -> Wrapped<Manager> {
 | 
			
		||||
        Wrapped::new(Manager { output: None })
 | 
			
		||||
    fn squeek_uiman_new(outputs: COutputs) -> Wrapped<Manager> {
 | 
			
		||||
        let uiman_raw = Wrapped::new(Manager::new());
 | 
			
		||||
        if !outputs.is_null() {
 | 
			
		||||
            let uiman = uiman_raw.clone_ref();
 | 
			
		||||
            let outputs = outputs.clone_ref();
 | 
			
		||||
            let mut outputs = outputs.borrow_mut();
 | 
			
		||||
            register_output_man(uiman, &mut outputs);
 | 
			
		||||
        }
 | 
			
		||||
        uiman_raw
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Used to size the layer surface containing all the OSK widgets.
 | 
			
		||||
@ -29,7 +60,7 @@ mod c {
 | 
			
		||||
        let uiman = uiman.clone_ref();
 | 
			
		||||
        let uiman = uiman.borrow();
 | 
			
		||||
        // TODO: what to do when there's no output?
 | 
			
		||||
        uiman.get_perceptual_height().unwrap_or(0)
 | 
			
		||||
        uiman.state.get_perceptual_height().unwrap_or(0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
@ -40,29 +71,75 @@ mod c {
 | 
			
		||||
    ) {
 | 
			
		||||
        let uiman = uiman.clone_ref();
 | 
			
		||||
        let mut uiman = uiman.borrow_mut();
 | 
			
		||||
        uiman.output = Some(output);
 | 
			
		||||
        uiman.set_output(output)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_uiman_set_surface(
 | 
			
		||||
        uiman: Wrapped<Manager>,
 | 
			
		||||
        surface: PhoshLayerSurface,
 | 
			
		||||
    ) {
 | 
			
		||||
        let uiman = uiman.clone_ref();
 | 
			
		||||
        let mut uiman = uiman.borrow_mut();
 | 
			
		||||
        // Surface is not state, so doesn't need to propagate updates.
 | 
			
		||||
        uiman.surface = Some(surface);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Stores current state of all things influencing what the UI should look like.
 | 
			
		||||
pub struct Manager {
 | 
			
		||||
    /// Shared output handle, current state updated whenever it's needed.
 | 
			
		||||
    // TODO: Stop assuming that the output never changes.
 | 
			
		||||
    // (There's no way for the output manager to update the ui manager.)
 | 
			
		||||
    // FIXME: Turn into an OutputState and apply relevant connections elsewhere.
 | 
			
		||||
    // Otherwise testability and predictablity is low.
 | 
			
		||||
    output: Option<OutputHandle>,
 | 
			
		||||
#[derive(Clone, PartialEq)]
 | 
			
		||||
pub struct ManagerState {
 | 
			
		||||
    current_output: Option<(OutputId, OutputState)>,
 | 
			
		||||
    //// Pixel size of the surface. Needs explicit updating.
 | 
			
		||||
    //surface_size: Option<Size>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Manager {
 | 
			
		||||
impl ManagerState {
 | 
			
		||||
    /// The largest ideal heigth for the keyboard as a whole
 | 
			
		||||
    /// judged by the ease of hitting targets within.
 | 
			
		||||
    /// Ideally related to finger size, the crammedness of the layout,
 | 
			
		||||
    /// distance from display, and motor skills of the user.
 | 
			
		||||
    // FIXME: Start by making this aware of display's dpi,
 | 
			
		||||
    // then layout number of rows.
 | 
			
		||||
    fn get_max_target_height(output: &OutputState) -> u32 {
 | 
			
		||||
        let layout_rows = 4; // FIXME: use number from layout.
 | 
			
		||||
        let px_size = output.get_pixel_size();
 | 
			
		||||
        let phys_size = output.get_phys_size();
 | 
			
		||||
 | 
			
		||||
        let finger_height_px = match (px_size, phys_size) {
 | 
			
		||||
            (Some(px_size), Some(phys_size)) => {
 | 
			
		||||
                // Fudged to result in 420px from the original design.
 | 
			
		||||
                // That gives about 9.5mm per finger height.
 | 
			
		||||
                // Maybe floats are not the best choice here,
 | 
			
		||||
                // but it gets rounded ASAP. Consider rationals.
 | 
			
		||||
                let keyboard_fraction_of_display: f64 = 420. / 1440.;
 | 
			
		||||
                let keyboard_mm = keyboard_fraction_of_display * 130.;
 | 
			
		||||
                let finger_height_mm = keyboard_mm / 4.;
 | 
			
		||||
                // TODO: Take into account target shape/area, not just height.
 | 
			
		||||
                finger_height_mm * px_size.height as f64 / phys_size.height as f64
 | 
			
		||||
            },
 | 
			
		||||
            (_, None) => output.scale as f64 * 52.5, // match total 420px at scale 2 from original design
 | 
			
		||||
            (None, Some(_)) => {
 | 
			
		||||
                log_print!(
 | 
			
		||||
                    logging::Level::Surprise,
 | 
			
		||||
                    "Output has physical size data but no pixel info",
 | 
			
		||||
                );
 | 
			
		||||
                output.scale as f64 * 52.5
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        (layout_rows as f64 * finger_height_px) as u32
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_perceptual_height(&self) -> Option<u32> {
 | 
			
		||||
        let output_info = (&self.output).as_ref()
 | 
			
		||||
            .and_then(|o| o.get_state())
 | 
			
		||||
            .map(|os| (os.scale as u32, os.get_pixel_size()));
 | 
			
		||||
        let output_info = (&self.current_output).as_ref()
 | 
			
		||||
            .map(|(_id, os)| (
 | 
			
		||||
                os.scale as u32,
 | 
			
		||||
                os.get_pixel_size(),
 | 
			
		||||
                ManagerState::get_max_target_height(&os),
 | 
			
		||||
            ));
 | 
			
		||||
        match output_info {
 | 
			
		||||
            Some((scale, Some(px_size))) => Some({
 | 
			
		||||
            Some((scale, Some(px_size), target_height)) => Some({
 | 
			
		||||
                let height = if (px_size.width < 720) & (px_size.width > 0) {
 | 
			
		||||
                    px_size.width * 7 / 12 // to match 360×210
 | 
			
		||||
                } else if px_size.width < 1080 {
 | 
			
		||||
@ -71,11 +148,94 @@ impl Manager {
 | 
			
		||||
                    360
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                // Don't exceed half the display size
 | 
			
		||||
                min(height, px_size.height / 2) / scale
 | 
			
		||||
                // Don't exceed half the display size.
 | 
			
		||||
                let height = min(height, px_size.height / 2);
 | 
			
		||||
                // Don't waste screen space by exceeding best target height.
 | 
			
		||||
                let height = min(height, target_height);
 | 
			
		||||
                height / scale
 | 
			
		||||
            }),
 | 
			
		||||
            Some((scale, None)) => Some(360 / scale),
 | 
			
		||||
            Some((scale, None, _)) => Some(360 / scale),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Manager {
 | 
			
		||||
    state: ManagerState,
 | 
			
		||||
    surface: Option<c::PhoshLayerSurface>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Manager {
 | 
			
		||||
    fn new() -> Manager {
 | 
			
		||||
        Manager {
 | 
			
		||||
            state: ManagerState { current_output: None, },
 | 
			
		||||
            surface: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn set_output(&mut self, output: OutputHandle) {
 | 
			
		||||
        let output_state = output.get_state()
 | 
			
		||||
            .or_warn(
 | 
			
		||||
                &mut logging::Print,
 | 
			
		||||
                logging::Problem::Bug,
 | 
			
		||||
                // This is really bad. It only happens when the layer surface
 | 
			
		||||
                // is placed, and it happens once.
 | 
			
		||||
                // The layer surface is on an output that can't be tracked.
 | 
			
		||||
                "Tried to set output that's not known to exist. Ignoring.",
 | 
			
		||||
            );
 | 
			
		||||
        self.state.current_output = output_state.map(
 | 
			
		||||
            |state| (output.get_id(), state)
 | 
			
		||||
        );
 | 
			
		||||
        // TODO: At the time of writing, this function is only used once,
 | 
			
		||||
        // before the layer surface is initialized.
 | 
			
		||||
        // Therefore it doesn't update anything. Maybe it should in the future,
 | 
			
		||||
        // if it sees more use.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_output_change(&mut self, output: OutputHandle) {
 | 
			
		||||
        let (id, output_state) = match &self.state.current_output {
 | 
			
		||||
            Some((id, state)) => {
 | 
			
		||||
                if *id != output.get_id() { return } // Not the current output.
 | 
			
		||||
                else { (id.clone(), state.clone()) }
 | 
			
		||||
            },
 | 
			
		||||
            None => return, // Keyboard isn't on any output.
 | 
			
		||||
        };
 | 
			
		||||
        if let Some(new_output_state) = output.get_state() {
 | 
			
		||||
            if new_output_state != output_state {
 | 
			
		||||
                let new_state = ManagerState {
 | 
			
		||||
                    current_output: Some((id.clone(), new_output_state)),
 | 
			
		||||
                    ..self.state.clone()
 | 
			
		||||
                };
 | 
			
		||||
                if let Some(surface) = &self.surface {
 | 
			
		||||
                    let new_height = new_state.get_perceptual_height();
 | 
			
		||||
                    if new_height != self.state.get_perceptual_height() {
 | 
			
		||||
                        // TODO: here hard-size the keyboard and suggestion box too.
 | 
			
		||||
                        match new_height {
 | 
			
		||||
                            Some(new_height) => unsafe {
 | 
			
		||||
                                c::squeek_manager_set_surface_height(
 | 
			
		||||
                                    *surface,
 | 
			
		||||
                                    new_height,
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                            None => log_print!(
 | 
			
		||||
                                logging::Level::Bug,
 | 
			
		||||
                                "Can't calculate new size",
 | 
			
		||||
                            ),
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                self.state = new_state;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn register_output_man(
 | 
			
		||||
    ui_man: Rc<RefCell<Manager>>,
 | 
			
		||||
    output_man: &mut Outputs,
 | 
			
		||||
) {
 | 
			
		||||
    let ui_man = ui_man.clone();
 | 
			
		||||
    output_man.set_update_cb(Box::new(move |output: OutputHandle| {
 | 
			
		||||
        let mut ui_man = ui_man.borrow_mut();
 | 
			
		||||
        ui_man.handle_output_change(output)
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,7 @@ pub mod c {
 | 
			
		||||
        }
 | 
			
		||||
        /// Extracts the reference to the data.
 | 
			
		||||
        /// It may cause problems if attempted in more than one place
 | 
			
		||||
        // FIXME: check for null
 | 
			
		||||
        pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
 | 
			
		||||
            Rc::from_raw(self.0)
 | 
			
		||||
        }
 | 
			
		||||
@ -107,6 +108,10 @@ pub mod c {
 | 
			
		||||
            Rc::into_raw(used_rc); // prevent dropping the original reference
 | 
			
		||||
            rc
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn is_null(&self) -> bool {
 | 
			
		||||
            self.0.is_null()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    impl<T> Clone for Wrapped<T> {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user