#include #include #include #include #include #include #include #include "generated.h" #include "rcm-client-main.h" #include "rcm-client-receive.h" #include "linux-input-keycodes.h" #define WINDOW_WIDTH 300 #define WINDOW_HEIGHT 300 struct remote { GList *buttons; GList *header_buttons; guint width; guint height; bool editing; bool active; }; struct rcbutton { char *name; guint row; guint col; guint rows; guint cols; GtkWidget *button; struct rcbutton *next; }; static struct remote *remote = NULL; #if 0 static void quick_message(GtkWindow *parent, gchar *message) { GtkWidget *dialog, *label, *content_area; GtkDialogFlags flags; flags = GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL; dialog = gtk_dialog_new_with_buttons("Message", parent, flags, "OK", GTK_RESPONSE_NONE, NULL); content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); label = gtk_label_new(message); g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog); gtk_container_add(GTK_CONTAINER(content_area), label); gtk_widget_show_all(dialog); } #endif /* Hacky McHack - Estimate the size needed for a button with ABCDEF */ static int calculate_img_size(GtkWidget *button) { static bool first = true; static int size; cairo_surface_t *surface; cairo_t *ct; PangoLayout *layout; PangoFontDescription *font_description; if (!first) return size; surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); ct = cairo_create(surface); layout = pango_cairo_create_layout(ct); gtk_style_context_get(gtk_widget_get_style_context(button), gtk_widget_get_state_flags(button), GTK_STYLE_PROPERTY_FONT, &font_description, NULL); pango_layout_set_font_description(layout, font_description); pango_font_description_free(font_description); pango_layout_set_text(layout, "ABCDEF", -1); pango_layout_get_pixel_size(layout, &size, NULL); g_object_unref(layout); cairo_destroy(ct); cairo_surface_destroy(surface); first = false; return size; } #define REMOTE_BUTTON_PADDING 2.0f static void create_button_img(GtkWidget *button, const char *keycode) { struct linux_input_keycode *lik; cairo_surface_t *surface; cairo_t *ct; PangoLayout *layout; PangoFontDescription *font_description; GtkWidget *img; const char *label = keycode; double x, y; double scale_x, scale_y; double scale = 1.0; GdkRGBA *color; gint pw, ph; int img_size; for (lik = &linux_input_keycodes[0]; lik->name; lik++) if (keycode && !strcmp(lik->name, keycode)) break; if (!lik->name) { printf("Unknown keycode! %s\n", keycode); label = ""; } if (lik->cairo_surface) goto out; img_size = calculate_img_size(button); surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, img_size, img_size); ct = cairo_create(surface); layout = pango_cairo_create_layout(ct); gtk_style_context_get(gtk_widget_get_style_context(button), gtk_widget_get_state_flags(button), GTK_STYLE_PROPERTY_FONT, &font_description, GTK_STYLE_PROPERTY_COLOR, &color, NULL); pango_layout_set_font_description(layout, font_description); pango_font_description_free(font_description); gdk_cairo_set_source_rgba(ct, color); gdk_rgba_free(color); pango_layout_set_text(layout, label, -1); pango_layout_get_pixel_size(layout, &pw, &ph); scale_x = ((float)img_size - REMOTE_BUTTON_PADDING * 2.0f) / (float)pw; scale_y = ((float)img_size - REMOTE_BUTTON_PADDING * 2.0f) / (float)ph; scale = scale_x < scale ? scale_x : scale; scale = scale_y < scale ? scale_y : scale; x = (float)img_size / 2.0f - ((float)pw / 2.0f) * scale; y = (float)img_size / 2.0f - ((float)ph / 2.0f) * scale; cairo_move_to(ct, x, y); cairo_scale(ct, scale, scale); pango_cairo_show_layout(ct, layout); g_object_unref(layout); cairo_destroy(ct); lik->cairo_surface = surface; out: img = gtk_image_new_from_surface(lik->cairo_surface); gtk_button_set_image(GTK_BUTTON(button), img); } static struct rcbutton * new_button_add(GtkGrid *grid, const gchar *protocol, guint64 scancode, const char *keycode, GdkRGBA *color, guint x, guint y, guint w, guint h) { struct rcbutton *rcb; gchar *tooltip; rcb = g_malloc0(sizeof(*rcb)); rcb->name = strdup(keycode); rcb->col = x; rcb->row = y; rcb->cols = w; rcb->rows = h; rcb->button = gtk_toggle_button_new(); gtk_widget_set_sensitive(rcb->button, false); tooltip = g_strdup_printf("Protocol: %s\nScancode: 0x%08" PRIx64 "\nKeycode : %s", protocol, scancode, keycode); gtk_widget_set_tooltip_text(rcb->button, tooltip); g_free(tooltip); remote->buttons = g_list_append(remote->buttons, rcb); gtk_grid_attach(grid, rcb->button, x, y, w, h); create_button_img(rcb->button, keycode); return rcb; } static GtkWidget *scrollda = NULL; void rcng_client_receive_destroy_ui() { if (scrollda) gtk_widget_destroy(scrollda); scrollda = NULL; } static gint own_page_num = -1; /* FIXME: These two functions should probably move to the core */ static void create_header_button(const gchar *label, const gchar *icon_name, GCallback callback, gpointer user_data) { GtkWidget *button; button = gtk_button_new_from_icon_name(icon_name, GTK_ICON_SIZE_BUTTON); gtk_button_set_label(GTK_BUTTON(button), label); gtk_button_set_always_show_image(GTK_BUTTON(button), TRUE); g_signal_connect(button, "clicked", callback, user_data); gtk_widget_show_all(button); gtk_header_bar_pack_end(GTK_HEADER_BAR(global->header), button); remote->header_buttons = g_list_prepend(remote->header_buttons, button); } static void remove_header_buttons(void) { GList *l; for (l = remote->header_buttons; l; l = l->next) gtk_widget_destroy(l->data); g_list_free(remote->header_buttons); remote->header_buttons = NULL; } static void toggle_edit_keymap(GtkButton *button, gpointer user_data); static void resize_keymap(GtkButton *button, gpointer user_data) { GtkWindow *parent = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))); GtkWidget *dialog; GtkWidget *content_area; GtkWidget *grid; GtkWidget *width_label; GtkAdjustment *width_adj; GtkWidget *width_input; GtkWidget *height_label; GtkAdjustment *height_adj; GtkWidget *height_input; gint r; gint width; gint height; dialog = gtk_dialog_new_with_buttons("Resize Keymap", parent, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, "OK", GTK_RESPONSE_ACCEPT, "Cancel", GTK_RESPONSE_REJECT, NULL); grid = gtk_grid_new(); gtk_grid_set_column_spacing(GTK_GRID(grid), 12); gtk_grid_set_row_spacing(GTK_GRID(grid), 12); gtk_container_set_border_width(GTK_CONTAINER(grid), 12); width_label = gtk_label_new("Width:"); gtk_widget_set_halign(width_label, GTK_ALIGN_END); width_adj = gtk_adjustment_new(remote->width, 1.0, 100.0, 1.0, 5.0, 0.0); width_input = gtk_spin_button_new(width_adj, 1.0, 0); gtk_grid_attach(GTK_GRID(grid), width_label, 1, 1, 1, 1); gtk_grid_attach(GTK_GRID(grid), width_input, 2, 1, 1, 1); height_label = gtk_label_new("Height:"); gtk_widget_set_halign(height_label, GTK_ALIGN_END); height_adj = gtk_adjustment_new(remote->height, 1.0, 100.0, 1.0, 5.0, 0.0); height_input = gtk_spin_button_new(height_adj, 1.0, 0); gtk_grid_attach(GTK_GRID(grid), height_label, 1, 2, 1, 1); gtk_grid_attach(GTK_GRID(grid), height_input, 2, 2, 1, 1); content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_container_set_border_width(GTK_CONTAINER(content_area), 12); gtk_container_add(GTK_CONTAINER(content_area), grid); gtk_widget_show_all(dialog); r = gtk_dialog_run(GTK_DIALOG(dialog)); if (r != GTK_RESPONSE_ACCEPT) goto out; width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(width_input)); height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(height_input)); printf("Asked to resize keylayout to %ix%i\n", width, height); out: gtk_widget_destroy(dialog); } static void set_edit_keymap(bool editing) { if (remote->width < 1 || remote->height < 1) return; remove_header_buttons(); if (editing) { create_header_button("Apply", "gtk-apply", G_CALLBACK(toggle_edit_keymap), &remote); create_header_button("Resize", "document-page-setup", G_CALLBACK(resize_keymap), &remote); } else create_header_button("Edit", "list-add", G_CALLBACK(toggle_edit_keymap), &remote); } static void toggle_edit_keymap(GtkButton *button, gpointer user_data) { remote->editing = !remote->editing; set_edit_keymap(remote->editing); } static void on_notebook_page_change(GtkNotebook *notebook, GtkWidget *page, guint new_page_num, gpointer user_data) { if (new_page_num != own_page_num) { remove_header_buttons(); remote->editing = false; remote->active = false; return; } remote->active = true; remote->editing = false; set_edit_keymap(remote->editing); g_print("Page change: %i -> %u\n", own_page_num, new_page_num); } void rcng_client_receive_keypress(GDBusObject *obj, const gchar *keycode, bool pressed) { GList *l; /* FIXME: Use object path, this is just test code */ if (!remote || !remote->active) return; for (l = remote->buttons; l; l = l->next) { struct rcbutton *rcb = l->data; if (strcmp(keycode, rcb->name)) continue; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rcb->button), pressed); } g_print("Key %s %s\n", keycode, pressed ? "pressed" : "released"); } void rcng_client_receive_init_ui(GDBusObject *new_hw) { static bool first = true; if (first) { g_signal_connect(global->notebook, "switch-page", G_CALLBACK(on_notebook_page_change), NULL); first = false; } rcng_client_receive_destroy_ui(); GDBusInterface *interface; interface = g_dbus_object_get_interface(new_hw, "org.gnome.RemoteControlManager.Device"); if (!interface) return; /* FIXME: if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a(uuuuaa{sv}))"))) */ gchar **keymaps = NULL; RCDevice *object = RCDEVICE(interface); rcdevice_call_list_keymaps_sync(object, &keymaps, NULL, NULL); g_print("I should look at keymap: %s\n", keymaps[0]); guint16 km_width; guint16 km_height; GVariant *km_entries = NULL; rcdevice_call_get_keymap_sync(object, keymaps[0], &km_width, &km_height, &km_entries, NULL, NULL); remote = g_malloc0(sizeof(*remote)); remote->width = km_width; remote->height = km_height; remote->editing = false; g_print("type of keymaps is %s\n", g_variant_get_type_string(km_entries)); GVariantIter iter; gsize n_items; GVariant *item; n_items = g_variant_iter_init(&iter, km_entries); g_print("Key items: %zu\n", n_items); guint row = 0; guint col = 0; scrollda = gtk_scrolled_window_new(NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(scrollda), 12); own_page_num = gtk_notebook_append_page(global->notebook, scrollda, gtk_label_new("Receive")); GtkWidget *rgrid; rgrid = gtk_grid_new(); gtk_widget_set_halign(rgrid, GTK_ALIGN_CENTER); gtk_grid_set_column_spacing(GTK_GRID(rgrid), 12); gtk_grid_set_row_spacing(GTK_GRID(rgrid), 12); gtk_container_add(GTK_CONTAINER(scrollda), rgrid); GtkWidget *fixed; fixed = gtk_fixed_new(); gtk_widget_set_name(fixed, "RemoteControlClientFixed"); gtk_grid_attach(GTK_GRID(rgrid), fixed, 1, 1, 1, 1); GtkWidget *frame; frame = gtk_frame_new(keymaps[0]); g_free(keymaps); gtk_widget_set_name(gtk_frame_get_label_widget(GTK_FRAME(frame)), "RemoteControlClientRemoteLabel"); gtk_widget_set_name(frame, "RemoteControlClientRemoteFrame"); gtk_fixed_put(GTK_FIXED(fixed), frame, 0, 0); GtkWidget *grid; grid = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(frame), grid); gtk_grid_set_column_spacing(GTK_GRID(grid), 12); gtk_grid_set_row_spacing(GTK_GRID(grid), 12); gtk_grid_set_column_homogeneous(GTK_GRID(grid), true); gtk_grid_set_row_homogeneous(GTK_GRID(grid), true); gtk_container_set_border_width(GTK_CONTAINER(grid), 12); while (g_variant_iter_loop (&iter, "@a{sv}", &item)) { gchar *type; gchar *protocol; guint64 scancode; gchar *keycode; g_variant_lookup(item, "type", "s", &type); g_variant_lookup(item, "protocol", "s", &protocol); g_variant_lookup(item, "scancode", "t", &scancode); g_variant_lookup(item, "keycode", "s", &keycode); if (!strcmp(type, "button")) new_button_add(GTK_GRID(grid), protocol, scancode, keycode, NULL, col, row, 1, 1); col++; if (col >= km_width) { col = 0; row++; } printf("Got a %s: %s:0x%08" PRIx64 ":%s\n", type, protocol, scancode, keycode); } gtk_widget_show_all(scrollda); }