Compare commits
14 Commits
squeekboar
...
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
|
typedef struct _EekGtkKeyboardPrivate
|
||||||
{
|
{
|
||||||
EekRenderer *renderer; // owned, nullable
|
EekRenderer *renderer; // owned, nullable
|
||||||
EekboardContextService *eekboard_context; // unowned reference
|
LayoutHolder *eekboard_context; // unowned reference
|
||||||
struct submission *submission; // unowned reference
|
struct submission *submission; // unowned reference
|
||||||
|
|
||||||
struct squeek_layout_state *layout; // unowned
|
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->width - allocation->x) * scale,
|
||||||
(uint32_t)(allocation->height - allocation->y) * scale);
|
(uint32_t)(allocation->height - allocation->y) * scale);
|
||||||
if (priv->layout->arrangement != new_type) {
|
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)
|
if (priv->renderer)
|
||||||
@ -158,7 +159,7 @@ static void drag(EekGtkKeyboard *self,
|
|||||||
if (!priv->keyboard) {
|
if (!priv->keyboard) {
|
||||||
return;
|
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,
|
priv->submission,
|
||||||
x, y, eek_renderer_get_transformation(priv->renderer), time,
|
x, y, eek_renderer_get_transformation(priv->renderer), time,
|
||||||
priv->eekboard_context, self);
|
priv->eekboard_context, self);
|
||||||
@ -170,7 +171,7 @@ static void release(EekGtkKeyboard *self, guint32 time)
|
|||||||
if (!priv->keyboard) {
|
if (!priv->keyboard) {
|
||||||
return;
|
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,
|
priv->submission,
|
||||||
eek_renderer_get_transformation(priv->renderer), time,
|
eek_renderer_get_transformation(priv->renderer), time,
|
||||||
priv->eekboard_context, self);
|
priv->eekboard_context, self);
|
||||||
@ -343,7 +344,7 @@ on_notify_keyboard (GObject *object,
|
|||||||
EekGtkKeyboard *self) {
|
EekGtkKeyboard *self) {
|
||||||
(void)spec;
|
(void)spec;
|
||||||
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self);
|
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) {
|
if (priv->renderer) {
|
||||||
eek_renderer_free(priv->renderer);
|
eek_renderer_free(priv->renderer);
|
||||||
}
|
}
|
||||||
@ -356,7 +357,7 @@ on_notify_keyboard (GObject *object,
|
|||||||
* Returns: a #GtkWidget
|
* Returns: a #GtkWidget
|
||||||
*/
|
*/
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
eek_gtk_keyboard_new (EekboardContextService *eekservice,
|
eek_gtk_keyboard_new (LayoutHolder *eekservice,
|
||||||
struct submission *submission,
|
struct submission *submission,
|
||||||
struct squeek_layout_state *layout)
|
struct squeek_layout_state *layout)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -48,7 +48,7 @@ struct _EekGtkKeyboardClass
|
|||||||
};
|
};
|
||||||
|
|
||||||
GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
|
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
|
G_END_DECLS
|
||||||
#endif /* EEK_GTK_KEYBOARD_H */
|
#endif /* EEK_GTK_KEYBOARD_H */
|
||||||
|
|||||||
@ -37,7 +37,7 @@ G_BEGIN_DECLS
|
|||||||
|
|
||||||
typedef struct _EekBounds EekBounds;
|
typedef struct _EekBounds EekBounds;
|
||||||
|
|
||||||
typedef struct _EekboardContextService EekboardContextService;
|
typedef struct _LayoutHolder LayoutHolder;
|
||||||
typedef struct _ServerContextService ServerContextService;
|
typedef struct _ServerContextService ServerContextService;
|
||||||
typedef struct _LevelKeyboard LevelKeyboard;
|
typedef struct _LevelKeyboard LevelKeyboard;
|
||||||
|
|
||||||
|
|||||||
@ -35,28 +35,17 @@ enum {
|
|||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
#define LAYOUT_HOLDER_GET_PRIVATE(obj) \
|
||||||
DESTROYED,
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolderPrivate))
|
||||||
LAST_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint signals[LAST_SIGNAL] = { 0, };
|
struct _LayoutHolderPrivate {
|
||||||
|
|
||||||
#define EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(obj) \
|
|
||||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
|
|
||||||
|
|
||||||
struct _EekboardContextServicePrivate {
|
|
||||||
LevelKeyboard *keyboard; // currently used keyboard
|
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
|
/// Needed for keymap changes after keyboard updates
|
||||||
struct submission *submission; // unowned
|
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
|
static void
|
||||||
eekboard_context_service_set_property (GObject *object,
|
eekboard_context_service_set_property (GObject *object,
|
||||||
@ -73,12 +62,12 @@ eekboard_context_service_set_property (GObject *object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
eekboard_context_service_get_property (GObject *object,
|
layout_holder_get_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
GValue *value,
|
GValue *value,
|
||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
{
|
{
|
||||||
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
|
LayoutHolder *context = LAYOUT_HOLDER(object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_KEYBOARD:
|
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
|
static void
|
||||||
settings_get_layout(GSettings *settings, char **type, char **layout)
|
settings_get_layout(GSettings *settings, char **type, char **layout)
|
||||||
{
|
{
|
||||||
@ -116,7 +98,8 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
gchar *layout_name = state->overlay_name;
|
||||||
|
|
||||||
if (layout_name == NULL) {
|
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) {
|
static void
|
||||||
g_autofree gchar *keyboard_layout = NULL;
|
layout_holder_init (LayoutHolder *self) {
|
||||||
g_autofree gchar *keyboard_type = NULL;
|
self->priv = LAYOUT_HOLDER_GET_PRIVATE(self);
|
||||||
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
|
static void
|
||||||
eekboard_context_service_constructed (GObject *object)
|
layout_holder_class_init (LayoutHolderClass *klass)
|
||||||
{
|
|
||||||
(void)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_service_class_init (EekboardContextServiceClass *klass)
|
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
GParamSpec *pspec;
|
GParamSpec *pspec;
|
||||||
|
|
||||||
gobject_class->constructed = eekboard_context_service_constructed;
|
|
||||||
gobject_class->set_property = eekboard_context_service_set_property;
|
gobject_class->set_property = eekboard_context_service_set_property;
|
||||||
gobject_class->get_property = eekboard_context_service_get_property;
|
gobject_class->get_property = layout_holder_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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EekboardContextService:keyboard:
|
* An #LevelKeyboard currently active in this context.
|
||||||
*
|
|
||||||
* An #EekKeyboard currently active in this context.
|
|
||||||
*/
|
*/
|
||||||
pspec = g_param_spec_pointer("keyboard",
|
pspec = g_param_spec_pointer("keyboard",
|
||||||
"Keyboard",
|
"Keyboard",
|
||||||
@ -237,10 +169,91 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
|
|||||||
pspec);
|
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";
|
const char *schema_name = "org.gnome.desktop.input-sources";
|
||||||
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
|
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
|
||||||
if (ssrc) {
|
if (ssrc) {
|
||||||
@ -250,10 +263,10 @@ eekboard_context_service_init (EekboardContextService *self)
|
|||||||
if (schema) {
|
if (schema) {
|
||||||
// Not referencing the found schema directly,
|
// Not referencing the found schema directly,
|
||||||
// because it's not clear how...
|
// because it's not clear how...
|
||||||
self->priv->settings = g_settings_new (schema_name);
|
tracker->gsettings = g_settings_new (schema_name);
|
||||||
gulong conn_id = g_signal_connect(self->priv->settings, "change-event",
|
gulong conn_id = g_signal_connect(tracker->gsettings, "change-event",
|
||||||
G_CALLBACK(settings_handle_layout_changed),
|
G_CALLBACK(handle_layout_changed),
|
||||||
self);
|
tracker);
|
||||||
if (conn_id == 0) {
|
if (conn_id == 0) {
|
||||||
g_warning ("Could not connect to gsettings updates, "
|
g_warning ("Could not connect to gsettings updates, "
|
||||||
"automatic layout changing unavailable");
|
"automatic layout changing unavailable");
|
||||||
@ -265,74 +278,6 @@ eekboard_context_service_init (EekboardContextService *self)
|
|||||||
} else {
|
} else {
|
||||||
g_warning("No gsettings schemas installed. Layout switching unavailable.");
|
g_warning("No gsettings schemas installed. Layout switching unavailable.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
settings_update_layout(tracker);
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,71 +30,60 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define EEKBOARD_CONTEXT_SERVICE_PATH "/org/fedorahosted/Eekboard/Context_%d"
|
#define EEKBOARD_TYPE_LAYOUT_HOLDER (layout_holder_get_type())
|
||||||
#define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context"
|
#define LAYOUT_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolder))
|
||||||
|
|
||||||
#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_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
|
#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(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_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))
|
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
|
||||||
|
|
||||||
|
|
||||||
typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
|
typedef struct _LayoutHolderClass LayoutHolderClass;
|
||||||
typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
|
typedef struct _LayoutHolderPrivate LayoutHolderPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EekboardContextService:
|
* Handles layout state, and virtual-keyboard.
|
||||||
*
|
|
||||||
* Handles layout state, gsettings, and virtual-keyboard.
|
|
||||||
*
|
*
|
||||||
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
|
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
|
||||||
* and the virtual keyboard protocol.
|
* 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;
|
GObject parent;
|
||||||
EekboardContextServicePrivate *priv;
|
LayoutHolderPrivate *priv;
|
||||||
struct squeek_layout_state *layout; // Unowned
|
struct squeek_layout_state *layout; // Unowned
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
struct _LayoutHolderClass {
|
||||||
* 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 {
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
GObjectClass parent_class;
|
GObjectClass parent_class;
|
||||||
|
|
||||||
/*< public >*/
|
|
||||||
/* signals */
|
|
||||||
void (*destroyed) (EekboardContextService *self);
|
|
||||||
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
/* padding */
|
/* padding */
|
||||||
gpointer pdummy[24];
|
gpointer pdummy[24];
|
||||||
};
|
};
|
||||||
|
|
||||||
GType eekboard_context_service_get_type
|
GType layout_holder_get_type(void) G_GNUC_CONST;
|
||||||
(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);
|
|
||||||
|
|
||||||
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);
|
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 hint,
|
||||||
uint32_t purpose);
|
uint32_t purpose);
|
||||||
void
|
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
|
G_END_DECLS
|
||||||
#endif /* EEKBOARD_CONTEXT_SERVICE_H */
|
#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 submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
||||||
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
||||||
struct wl_seat *seat,
|
struct wl_seat *seat,
|
||||||
EekboardContextService *state) {
|
LayoutHolder *state) {
|
||||||
struct zwp_input_method_v2 *im = NULL;
|
struct zwp_input_method_v2 *im = NULL;
|
||||||
if (immanager) {
|
if (immanager) {
|
||||||
im = zwp_input_method_manager_v2_get_input_method(immanager, seat);
|
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 submission *submission,
|
||||||
struct transformation widget_to_layout,
|
struct transformation widget_to_layout,
|
||||||
uint32_t timestamp,
|
uint32_t timestamp,
|
||||||
EekboardContextService *manager,
|
LayoutHolder *manager,
|
||||||
EekGtkKeyboard *ui_keyboard);
|
EekGtkKeyboard *ui_keyboard);
|
||||||
void squeek_layout_release_all_only(struct squeek_layout *layout,
|
void squeek_layout_release_all_only(struct squeek_layout *layout,
|
||||||
struct submission *submission,
|
struct submission *submission,
|
||||||
@ -61,7 +61,7 @@ void squeek_layout_drag(struct squeek_layout *layout,
|
|||||||
struct submission *submission,
|
struct submission *submission,
|
||||||
double x_widget, double y_widget,
|
double x_widget, double y_widget,
|
||||||
struct transformation widget_to_layout,
|
struct transformation widget_to_layout,
|
||||||
uint32_t timestamp, EekboardContextService *manager,
|
uint32_t timestamp, LayoutHolder *manager,
|
||||||
EekGtkKeyboard *ui_keyboard);
|
EekGtkKeyboard *ui_keyboard);
|
||||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
|
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);
|
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
|
||||||
|
|||||||
@ -15,6 +15,7 @@ sources = [
|
|||||||
'dbus.c',
|
'dbus.c',
|
||||||
'imservice.c',
|
'imservice.c',
|
||||||
'server-context-service.c',
|
'server-context-service.c',
|
||||||
|
'ui_manager.c',
|
||||||
'wayland.c',
|
'wayland.c',
|
||||||
'../eek/eek.c',
|
'../eek/eek.c',
|
||||||
'../eek/eek-element.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 */
|
/*! Managing Wayland outputs */
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use ::logging;
|
use ::logging;
|
||||||
|
|
||||||
@ -17,7 +22,7 @@ pub mod c {
|
|||||||
// Defined in C
|
// Defined in C
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, PartialEq, Copy)]
|
#[derive(Clone, PartialEq, Copy, Hash)]
|
||||||
pub struct WlOutput(*const c_void);
|
pub struct WlOutput(*const c_void);
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -63,7 +68,7 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Map to `wl_output.transform` values
|
/// Map to `wl_output.transform` values
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Transform {
|
pub enum Transform {
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
Rotated90 = 1,
|
Rotated90 = 1,
|
||||||
@ -103,7 +108,7 @@ pub mod c {
|
|||||||
) -> i32;
|
) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
type COutputs = ::util::c::Wrapped<Outputs>;
|
pub type COutputs = ::util::c::Wrapped<Outputs>;
|
||||||
|
|
||||||
/// A stable reference to an output.
|
/// A stable reference to an output.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -121,6 +126,10 @@ pub mod c {
|
|||||||
let outputs = outputs.borrow();
|
let outputs = outputs.borrow();
|
||||||
find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
|
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
|
// Defined in Rust
|
||||||
@ -129,7 +138,7 @@ pub mod c {
|
|||||||
outputs: COutputs,
|
outputs: COutputs,
|
||||||
wl_output: WlOutput,
|
wl_output: WlOutput,
|
||||||
_x: i32, _y: i32,
|
_x: i32, _y: i32,
|
||||||
_phys_width: i32, _phys_height: i32,
|
phys_width: i32, phys_height: i32,
|
||||||
_subpixel: i32,
|
_subpixel: i32,
|
||||||
_make: *const c_char, _model: *const c_char,
|
_make: *const c_char, _model: *const c_char,
|
||||||
transform: i32,
|
transform: i32,
|
||||||
@ -145,8 +154,23 @@ pub mod c {
|
|||||||
let output_state: Option<&mut OutputState>
|
let output_state: Option<&mut OutputState>
|
||||||
= find_output_mut(&mut collection, wl_output)
|
= find_output_mut(&mut collection, wl_output)
|
||||||
.map(|o| &mut o.pending);
|
.map(|o| &mut o.pending);
|
||||||
|
|
||||||
match output_state {
|
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!(
|
None => log_print!(
|
||||||
logging::Level::Warning,
|
logging::Level::Warning,
|
||||||
"Got geometry on unknown output",
|
"Got geometry on unknown output",
|
||||||
@ -187,19 +211,27 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern fn outputs_handle_done(
|
extern fn outputs_handle_done(
|
||||||
outputs: COutputs,
|
outputs_raw: COutputs,
|
||||||
wl_output: WlOutput,
|
wl_output: WlOutput,
|
||||||
) {
|
) {
|
||||||
let outputs = outputs.clone_ref();
|
let outputs = outputs_raw.clone_ref();
|
||||||
let mut collection = outputs.borrow_mut();
|
{
|
||||||
let output = find_output_mut(&mut collection, wl_output);
|
let mut collection = RefCell::borrow_mut(&outputs);
|
||||||
match output {
|
let output = find_output_mut(&mut collection, wl_output);
|
||||||
Some(output) => { output.current = output.pending.clone(); }
|
match output {
|
||||||
None => log_print!(
|
Some(output) => { output.current = output.pending.clone(); }
|
||||||
logging::Level::Warning,
|
None => log_print!(
|
||||||
"Got done on unknown output",
|
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(
|
extern fn outputs_handle_scale(
|
||||||
@ -224,7 +256,10 @@ pub mod c {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_outputs_new() -> COutputs {
|
fn squeek_outputs_new() -> COutputs {
|
||||||
COutputs::new(Outputs { outputs: Vec::new() })
|
COutputs::new(Outputs {
|
||||||
|
outputs: Vec::new(),
|
||||||
|
update_cb: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -293,22 +328,29 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generic size
|
/// Generic size
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct SizeMM {
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
/// wl_output mode
|
/// wl_output mode
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
struct Mode {
|
struct Mode {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct OutputState {
|
pub struct OutputState {
|
||||||
current_mode: Option<Mode>,
|
current_mode: Option<Mode>,
|
||||||
|
phys_size: Option<SizeMM>,
|
||||||
transform: Option<c::Transform>,
|
transform: Option<c::Transform>,
|
||||||
pub scale: i32,
|
pub scale: i32,
|
||||||
}
|
}
|
||||||
@ -323,6 +365,7 @@ impl OutputState {
|
|||||||
fn uninitialized() -> OutputState {
|
fn uninitialized() -> OutputState {
|
||||||
OutputState {
|
OutputState {
|
||||||
current_mode: None,
|
current_mode: None,
|
||||||
|
phys_size: None,
|
||||||
transform: None,
|
transform: None,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
}
|
}
|
||||||
@ -334,6 +377,35 @@ impl OutputState {
|
|||||||
OutputState {
|
OutputState {
|
||||||
current_mode: Some(Mode { width, height } ),
|
current_mode: Some(Mode { width, height } ),
|
||||||
transform: Some(transform),
|
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: _,
|
scale: _,
|
||||||
} => Some(
|
} => Some(
|
||||||
match transform {
|
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 {
|
pub struct Output {
|
||||||
output: c::WlOutput,
|
output: c::WlOutput,
|
||||||
pending: OutputState,
|
pending: OutputState,
|
||||||
current: 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 {
|
pub struct Outputs {
|
||||||
outputs: Vec<Output>,
|
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 {
|
struct _ServerContextService {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
|
|
||||||
EekboardContextService *state; // unowned
|
LayoutHolder *state; // unowned
|
||||||
/// Needed for instantiating the widget
|
/// Needed for instantiating the widget
|
||||||
struct submission *submission; // unowned
|
struct submission *submission; // unowned
|
||||||
struct squeek_layout_state *layout;
|
struct squeek_layout_state *layout;
|
||||||
@ -139,6 +139,7 @@ make_window (ServerContextService *context)
|
|||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
squeek_uiman_set_surface(context->manager, context->window);
|
||||||
g_object_connect (context->window,
|
g_object_connect (context->window,
|
||||||
"signal::destroy", G_CALLBACK(on_destroy), context,
|
"signal::destroy", G_CALLBACK(on_destroy), context,
|
||||||
"signal::map", G_CALLBACK(on_notify_map), context,
|
"signal::map", G_CALLBACK(on_notify_map), context,
|
||||||
@ -311,7 +312,7 @@ server_context_service_init (ServerContextService *state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerContextService *
|
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);
|
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
|
||||||
ui->submission = submission;
|
ui->submission = submission;
|
||||||
|
|||||||
@ -37,7 +37,7 @@ typedef struct _ServerContextService ServerContextService;
|
|||||||
GType server_context_service_get_type
|
GType server_context_service_get_type
|
||||||
(void) G_GNUC_CONST;
|
(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 *);
|
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
|
||||||
void server_context_service_show_keyboard (ServerContextService *context);
|
void server_context_service_show_keyboard (ServerContextService *context);
|
||||||
void server_context_service_hide_keyboard (ServerContextService *context);
|
void server_context_service_hide_keyboard (ServerContextService *context);
|
||||||
|
|||||||
@ -42,11 +42,12 @@
|
|||||||
struct squeekboard {
|
struct squeekboard {
|
||||||
struct squeek_wayland wayland; // Just hooks.
|
struct squeek_wayland wayland; // Just hooks.
|
||||||
DBusHandler *dbus_handler; // Controls visibility of the OSK.
|
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
|
ServerContextService *ui_context; // mess, includes the entire UI
|
||||||
struct submission *submission; // Wayland text input handling.
|
struct submission *submission; // Wayland text input handling.
|
||||||
struct squeek_layout_state layout_choice; // Currently wanted layout.
|
struct squeek_layout_state layout_choice; // Currently wanted layout.
|
||||||
struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice
|
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");
|
g_warning("Wayland input method interface not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.ui_manager = squeek_uiman_new();
|
instance.ui_manager = squeek_uiman_new(instance.wayland.outputs);
|
||||||
|
|
||||||
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
|
|
||||||
|
|
||||||
|
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
|
// set up dbus
|
||||||
|
|
||||||
// TODO: make dbus errors non-always-fatal
|
// 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.submission = get_submission(instance.wayland.input_method_manager,
|
||||||
instance.wayland.virtual_keyboard_manager,
|
instance.wayland.virtual_keyboard_manager,
|
||||||
instance.wayland.seat,
|
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(
|
ServerContextService *ui_context = server_context_service_new(
|
||||||
instance.settings_context,
|
instance.layout_holder,
|
||||||
instance.submission,
|
instance.submission,
|
||||||
&instance.layout_choice,
|
&instance.layout_choice,
|
||||||
instance.ui_manager);
|
instance.ui_manager);
|
||||||
@ -299,7 +300,6 @@ main (int argc, char **argv)
|
|||||||
if (instance.dbus_handler) {
|
if (instance.dbus_handler) {
|
||||||
dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
|
dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
|
||||||
}
|
}
|
||||||
eekboard_context_service_set_ui(instance.settings_context, instance.ui_context);
|
|
||||||
|
|
||||||
session_register();
|
session_register();
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,10 @@ struct submission;
|
|||||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
||||||
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
||||||
struct wl_seat *seat,
|
struct wl_seat *seat,
|
||||||
EekboardContextService *state);
|
LayoutHolder *state);
|
||||||
|
|
||||||
// Defined in Rust
|
// 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_ui(struct submission *self, ServerContextService *ui_context);
|
||||||
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
|
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
|
||||||
#endif
|
#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__
|
#define UI_MANAGER__
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include "eek/layersurface.h"
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
struct ui_manager;
|
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_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);
|
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -7,17 +7,48 @@
|
|||||||
* Coordinates this based on information collated from all possible sources.
|
* Coordinates this based on information collated from all possible sources.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use ::logging;
|
||||||
|
use ::outputs::{ OutputId, Outputs, OutputState};
|
||||||
use ::outputs::c::OutputHandle;
|
use ::outputs::c::OutputHandle;
|
||||||
|
|
||||||
|
// Traits
|
||||||
|
use ::logging::Warn;
|
||||||
|
|
||||||
mod c {
|
mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use ::outputs::c::COutputs;
|
||||||
use ::util::c::Wrapped;
|
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]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_uiman_new() -> Wrapped<Manager> {
|
fn squeek_uiman_new(outputs: COutputs) -> Wrapped<Manager> {
|
||||||
Wrapped::new(Manager { output: None })
|
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.
|
/// 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.clone_ref();
|
||||||
let uiman = uiman.borrow();
|
let uiman = uiman.borrow();
|
||||||
// TODO: what to do when there's no output?
|
// 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]
|
#[no_mangle]
|
||||||
@ -40,29 +71,75 @@ mod c {
|
|||||||
) {
|
) {
|
||||||
let uiman = uiman.clone_ref();
|
let uiman = uiman.clone_ref();
|
||||||
let mut uiman = uiman.borrow_mut();
|
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.
|
/// Stores current state of all things influencing what the UI should look like.
|
||||||
pub struct Manager {
|
#[derive(Clone, PartialEq)]
|
||||||
/// Shared output handle, current state updated whenever it's needed.
|
pub struct ManagerState {
|
||||||
// TODO: Stop assuming that the output never changes.
|
current_output: Option<(OutputId, OutputState)>,
|
||||||
// (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>,
|
|
||||||
//// Pixel size of the surface. Needs explicit updating.
|
//// Pixel size of the surface. Needs explicit updating.
|
||||||
//surface_size: Option<Size>,
|
//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> {
|
fn get_perceptual_height(&self) -> Option<u32> {
|
||||||
let output_info = (&self.output).as_ref()
|
let output_info = (&self.current_output).as_ref()
|
||||||
.and_then(|o| o.get_state())
|
.map(|(_id, os)| (
|
||||||
.map(|os| (os.scale as u32, os.get_pixel_size()));
|
os.scale as u32,
|
||||||
|
os.get_pixel_size(),
|
||||||
|
ManagerState::get_max_target_height(&os),
|
||||||
|
));
|
||||||
match output_info {
|
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) {
|
let height = if (px_size.width < 720) & (px_size.width > 0) {
|
||||||
px_size.width * 7 / 12 // to match 360×210
|
px_size.width * 7 / 12 // to match 360×210
|
||||||
} else if px_size.width < 1080 {
|
} else if px_size.width < 1080 {
|
||||||
@ -71,11 +148,94 @@ impl Manager {
|
|||||||
360
|
360
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't exceed half the display size
|
// Don't exceed half the display size.
|
||||||
min(height, px_size.height / 2) / scale
|
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,
|
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.
|
/// Extracts the reference to the data.
|
||||||
/// It may cause problems if attempted in more than one place
|
/// It may cause problems if attempted in more than one place
|
||||||
|
// FIXME: check for null
|
||||||
pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
|
pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
|
||||||
Rc::from_raw(self.0)
|
Rc::from_raw(self.0)
|
||||||
}
|
}
|
||||||
@ -107,6 +108,10 @@ pub mod c {
|
|||||||
Rc::into_raw(used_rc); // prevent dropping the original reference
|
Rc::into_raw(used_rc); // prevent dropping the original reference
|
||||||
rc
|
rc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.0.is_null()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Clone for Wrapped<T> {
|
impl<T> Clone for Wrapped<T> {
|
||||||
|
|||||||
Reference in New Issue
Block a user