diff options
Diffstat (limited to 'rcm-client-receive.c')
-rw-r--r-- | rcm-client-receive.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/rcm-client-receive.c b/rcm-client-receive.c new file mode 100644 index 0000000..8abc64a --- /dev/null +++ b/rcm-client-receive.c @@ -0,0 +1,612 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <math.h> +#include <inttypes.h> +#include <gtk/gtk.h> + +#include "generated.h" +#include "rcm-client-main.h" +#include "rcm-client-receive.h" + +#define WINDOW_WIDTH 300 +#define WINDOW_HEIGHT 300 + +struct remote { + GList *buttons; + guint width; + guint height; + struct rcbutton *hover; +}; + +struct rcbutton { + char *name; + bool clicked; + guint x; + guint y; + guint row; + guint col; + guint rows; + guint cols; + guint width; + guint height; + guint padding; + guint corner_radius; + struct rcbutton *next; + GdkRGBA *color; + void (*on_hover)(struct rcbutton *rcb, GtkWidget *widget, bool hover); +}; + +static struct remote *remote = NULL; + +static void +curve_rectangle(cairo_t * cr, gdouble x0, gdouble y0, + gdouble width, gdouble height, gdouble radius) +{ + gdouble x1, y1; + + if (!width || !height) + return; + + x1 = x0 + width; + y1 = y0 + height; + + radius = MIN (radius, MIN (width / 2, height / 2)); + + cairo_move_to (cr, x0, y0 + radius); + cairo_arc (cr, x0 + radius, y0 + radius, radius, M_PI, + 3 * M_PI / 2); + cairo_line_to (cr, x1 - radius, y0); + cairo_arc (cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, + 2 * M_PI); + cairo_line_to (cr, x1, y1 - radius); + cairo_arc (cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2); + cairo_line_to (cr, x0 + radius, y1); + cairo_arc (cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI); + + cairo_close_path (cr); +} + +GdkRGBA *c; +GdkRGBA *cd; +GdkRGBA *cre; +GdkRGBA *cb; +GdkRGBA cgreen; +GdkRGBA cred; + +static void +draw_curve_rectangle(cairo_t * cr, struct rcbutton *rcb) +{ + double tx, ty; + cairo_text_extents_t extents; + const char *label = rcb->name ? rcb->name : "?"; + + curve_rectangle(cr, rcb->x, rcb->y, + rcb->width, rcb->height, rcb->corner_radius); + + if (rcb->color) + gdk_cairo_set_source_rgba(cr, rcb->color); + else + gdk_cairo_set_source_rgba(cr, rcb->clicked ? cre : cd); + cairo_fill_preserve(cr); + + gdk_cairo_set_source_rgba(cr, cb); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + /* + if (filled) + cairo_fill(cr); + else { + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + } + */ + + cairo_save(cr); + cairo_rectangle(cr, + rcb->x + rcb->padding, + rcb->y + rcb->padding, + rcb->width - rcb->padding * 2, + rcb->height - rcb->padding * 2); + cairo_clip(cr); + cairo_set_font_size(cr, 13); + cairo_text_extents (cr, label, &extents); + tx = rcb->x + (rcb->width / 2) - (extents.width / 2 + extents.x_bearing); + ty = rcb->y + (rcb->height / 2) - (extents.height / 2 + extents.y_bearing); + cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); + cairo_move_to(cr, tx, ty); + cairo_show_text(cr, label); + cairo_restore(cr); + cairo_new_sub_path(cr); +} + +#define SPACING 5 +#define PADDING 5 +#define BW 56 +#define BH 56 + +static gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + struct rcbutton *rcb; + guint w = 0, h = 0; + + /* Set color for background */ + gdk_cairo_set_source_rgba(cr, c); + + /* fill in the background color*/ + cairo_paint(cr); + + GList *l; + + for (l = remote->buttons; l; l = l->next) { + rcb = l->data; + + draw_curve_rectangle(cr, rcb); + + if (rcb->x + rcb->width > w) + w = rcb->x + rcb->width; + + if (rcb->y + rcb->height > h) + h = rcb->y + rcb->height; + } + + w += (SPACING + BW) / 2; + h += (SPACING + BH) / 2; + + /*->name ? rcb->name : "?", false, + rcb->clicked ? cre : cd, + rcb->x, rcb->y, rcb->width, rcb->height, + rcb->padding, rcb->corner_radius); +*/ + gtk_widget_set_size_request(widget, w, h); + gtk_widget_set_halign(widget, GTK_ALIGN_CENTER); + gtk_widget_set_valign(widget, GTK_ALIGN_CENTER); + gtk_widget_set_margin_top(widget, BH / 2); + gtk_widget_set_margin_bottom(widget, BH / 2); + gtk_widget_set_margin_start(widget, BW / 2); + gtk_widget_set_margin_end(widget, BW / 2); + + + return FALSE; +} + +#if 0 +static struct rcbutton *new_button(const char *name, guint row, guint col) +{ + struct rcbutton *rcb; + + rcb = g_malloc(sizeof(*rcb)); + rcb->name = strdup(name); + rcb->clicked = false; + rcb->x = (SPACING + BW) * (col) + (SPACING + BW) / 2; + rcb->y = (SPACING + BH) * (row) + (SPACING + BH) / 2; + rcb->width = BW; + rcb->height = BH; + rcb->padding = PADDING; + rcb->corner_radius = 3; + rcb->color = NULL; + return rcb; +} +#endif + +static void +button_calculate_position(struct rcbutton *rcb) +{ + rcb->x = (SPACING + BW) * (rcb->col) + (SPACING + BW) / 2; + rcb->y = (SPACING + BH) * (rcb->row) + (SPACING + BH) / 2; + rcb->width = BW + (rcb->cols - 1) * (BW + SPACING); + rcb->height = BH + (rcb->rows - 1) * (BH + SPACING); +} + +static void +move_buttons(gint row, gint col) +{ + GList *l; + + for (l = remote->buttons; l; l = l->next) { + struct rcbutton *rcb = l->data; + + rcb->row += row; + rcb->col += col; + button_calculate_position(rcb); + } +} + +static struct rcbutton * +new_button_add(const char *name, GdkRGBA *color, + guint x, guint y, guint w, guint h) +{ + struct rcbutton *rcb; + + rcb = g_malloc0(sizeof(*rcb)); + rcb->name = strdup(name); + rcb->clicked = false; + rcb->col = x; + rcb->row = y; + rcb->cols = w; + rcb->rows = h; + button_calculate_position(rcb); + rcb->padding = PADDING; + rcb->corner_radius = 3; + rcb->color = color; + remote->buttons = g_list_append(remote->buttons, rcb); + remote->width = MAX(remote->width, w); + remote->height = MAX(remote->height, h); + return rcb; +} + +#if 0 +static struct rcbutton *new_button_addr(const char *name, guint row, guint col) +{ + struct rcbutton *rcb; + + rcb = g_malloc(sizeof(*rcb)); + rcb->name = strdup(name); + rcb->clicked = false; + rcb->x = (SPACING + BW) / 2; + rcb->y = (SPACING + BH) / 2; + rcb->width = (SPACING + BW) * (col) - SPACING; + rcb->height = BH; + rcb->padding = PADDING; + rcb->corner_radius = 3; + rcb->color = &cred; + return rcb; +} +#endif +static void +button_redraw(struct rcbutton *rcb, GtkWidget *widget) +{ + printf("Asked to redraw: X,Y,W,H - %u,%u,%u,%u\n", + rcb->x - 1, rcb->y - 1, rcb->width + 2, rcb->height + 2); + gtk_widget_queue_draw_area(widget, rcb->x - 1, rcb->y - 1, + rcb->width + 2, rcb->height + 2); +} + +static gboolean +button_press_event_cb(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + struct rcbutton *rcb; + GList *l; + char buf[1024]; + + if (event->button != GDK_BUTTON_PRIMARY) + return true; + + for (l = remote->buttons; l; l = l->next) { + rcb = l->data; + + if (event->x < rcb->x || event->x > (rcb->x + rcb->width)) + continue; + + if (event->y < rcb->y || event->y > (rcb->y + rcb->height)) + continue; + + rcb->clicked = true; + button_redraw(rcb, widget); + sprintf(buf, "Button at %ux%u clicked\n", rcb->x, rcb->y); + poke_objects(); + poke_objects2(); + } + + return true; +} + +static gboolean +button_release_event_cb(GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + struct rcbutton *rcb; + GList *l; + + if (event->button != GDK_BUTTON_PRIMARY) + return true; + + for (l = remote->buttons; l; l = l->next) { + rcb = l->data; + + if (!rcb->clicked) + continue; + + rcb->clicked = false; + + button_redraw(rcb, widget); + } + + return true; +} + +static gboolean +motion_notify_event_cb (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + struct rcbutton *rcb; + static GdkCursor *cursor = NULL; + GdkWindow* win = gtk_widget_get_parent_window(widget); + GList *l; + + if (!cursor) + cursor = gdk_cursor_new_for_display(gdk_window_get_display(win), GDK_HAND1); + + for (l = remote->buttons; l; l = l->next) { + rcb = l->data; + + if (event->x < rcb->x || event->x > (rcb->x + rcb->width)) + continue; + + if (event->y < rcb->y || event->y > (rcb->y + rcb->height)) + continue; + + break; + } + + if (!l) + rcb = NULL; + + if (remote->hover == rcb) + return true; + + gdk_window_set_cursor(win, rcb ? cursor : NULL); + + if (remote->hover && remote->hover->on_hover) { + remote->hover->on_hover(remote->hover, widget, false); + remote->hover = NULL; + } + + if (rcb && rcb->on_hover) { + rcb->on_hover(rcb, widget, true); + remote->hover = rcb; + } + + return true; +} + +static GtkWidget *scrollda = NULL; + +void rcng_client_receive_destroy_ui() +{ + if (scrollda) + gtk_widget_destroy(scrollda); + + scrollda = NULL; +} + +static gint own_page_num = -1; +static GtkWidget *header_button = NULL; +static GtkWidget *da; + +#if 0 +static void +edit_keymap_done(GtkButton *button, gpointer user_data) +{ + +} +#endif +static void +on_hover_delete(struct rcbutton *rcb, GtkWidget *widget, bool hover) +{ + if (hover) { + if (rcb->col == 0 && rcb->cols == 1) { + rcb->cols = 2 + remote->width; + button_calculate_position(rcb); + button_redraw(rcb, widget); + } else if (rcb->row == 0 && rcb->rows == 1) { + rcb->rows = 2 + remote->height; + button_calculate_position(rcb); + button_redraw(rcb, widget); + } + } else { + if (rcb->col == 0 && rcb->cols != 1) { + button_redraw(rcb, widget); + rcb->cols = 1; + button_calculate_position(rcb); + } else if (rcb->row == 0 && rcb->rows != 1) { + button_redraw(rcb, widget); + rcb->rows = 1; + button_calculate_position(rcb); + } + } + +} + +static void +edit_keymap(GtkButton *button, gpointer user_data) +{ + guint i; + gboolean *do_edit = user_data; + struct rcbutton *rcb; + + g_print("Button clicked\n"); + g_print("Remote width: %u x %u\n", remote->width, remote->height); + if (remote->width < 1 || remote->height < 1) + return; + + if (!*do_edit) { + move_buttons(2, 2); + new_button_add("+", &cgreen, 2, 1, remote->width, 1); + new_button_add("+", &cgreen, 2, 2 + remote->height, remote->width, 1); + new_button_add("+", &cgreen, 1, 2, 1, remote->height); + new_button_add("+", &cgreen, 2 + remote->width, 2, 1, remote->height); + + for (i = 0; i < remote->height; i++) { + rcb = new_button_add("-", &cred, 0, 2 + i, 1, 1); + rcb->on_hover = on_hover_delete; + } + + for (i = 0; i < remote->width; i++) { + rcb = new_button_add("-", &cred, 2 + i, 0, 1, 1); + rcb->on_hover = on_hover_delete; + } + + /* + new_button_add("+", &cgreen, 5, 2, 1, 7); + new_button_add("+", &cgreen, 2, 4, 1, 1); + new_button_add("+", &cgreen, 2, 4, 10, 10); + new_button_add("+", &cgreen, 2, 4, 10, 10); + new_button_add("-", &cred, 2, 2, 0, 0); + new_button_add("Del", &cred, 3, 3, 0, 0); + */ + + gtk_widget_queue_draw(da); + *do_edit = true; + } else { + GList *l = remote->buttons; + + while (l) { + GList *next = l->next; + struct rcbutton *rcb = l->data; + + if (rcb->color) { + g_free(rcb); + remote->buttons = g_list_delete_link (remote->buttons, l); + } + + l = next; + } + move_buttons(-2, -2); + gtk_widget_queue_draw(da); + *do_edit = false; + + } +} + +static void +on_notebook_page_change(GtkNotebook *notebook, GtkWidget *page, guint new_page_num, + gpointer user_data) +{ + static gboolean do_edit = false; + + if (new_page_num != own_page_num) { + if (header_button) { + gtk_widget_destroy(header_button); + header_button = NULL; + do_edit = false; + } + return; + } + + GtkWidget *test = gtk_button_new_from_icon_name("gtk-add", GTK_ICON_SIZE_BUTTON); + gtk_button_set_label(GTK_BUTTON(test), "Edit Keymap"); + gtk_button_set_always_show_image(GTK_BUTTON(test), TRUE); + g_signal_connect(test, "clicked", G_CALLBACK(edit_keymap), &do_edit); + gtk_widget_show_all(test); + gtk_header_bar_pack_end(GTK_HEADER_BAR(global->header), test); + header_button = test; + g_print("Page change: %i -> %u\n", own_page_num, new_page_num); +} + +void rcng_client_receive_init_ui(GDBusObject *new_hw) +{ + GtkStateFlags state; + GtkStyleContext *style_context; + 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); + + g_free(keymaps); + remote = g_malloc0(sizeof(*remote)); + remote->width = km_width; + remote->height = km_height; + + g_print("type of keymaps is %s\n", g_variant_get_type_string(km_entries)); + GVariantIter iter; + gsize n_items; + GVariant *item; + + style_context = gtk_widget_get_style_context(GTK_WIDGET(global->window)); + state = gtk_widget_get_state_flags(GTK_WIDGET(global->window)); + gtk_style_context_get(style_context, state, "background-color", &c, NULL); + + cd = gdk_rgba_copy(c); + cd->red *= 0.7; + cd->green *= 0.7; + cd->blue *= 0.7; + + cre = gdk_rgba_copy(c); + cre->red *= 2.0; + + cb = gdk_rgba_copy(c); + cb->red = 0.0; + cb->green = 0.0; + cb->blue = 0.0; + cb->alpha = 1.0; + + gdk_rgba_parse(&cgreen, "#a9b68f"); + gdk_rgba_parse(&cred, "#cf8989"); + cred.alpha = 0.8; + + n_items = g_variant_iter_init(&iter, km_entries); + g_print("Key items: %zu\n", n_items); + guint row = 0; + guint col = 0; + + while (g_variant_iter_loop (&iter, "@a{sv}", &item)) { + gchar *protocol; + guint64 scancode; + gchar *keycode; + + g_variant_lookup(item, "protocol", "s", &protocol); + g_variant_lookup(item, "scancode", "t", &scancode); + g_variant_lookup(item, "keycode", "s", &keycode); + + new_button_add(keycode, NULL, col, row, 1, 1); + + col++; + if (col >= km_width) { + col = 0; + row++; + } + + printf("Got a key: %s:0x%08" PRIx64 ":%s\n", protocol, scancode, keycode); + } + + da = gtk_drawing_area_new(); +// gtk_widget_set_size_request (da, WINDOW_WIDTH, WINDOW_HEIGHT); + g_signal_connect (da, "draw", G_CALLBACK(draw_cb), NULL); + + g_signal_connect(da, "button-press-event", + G_CALLBACK (button_press_event_cb), NULL); + + g_signal_connect(da, "button-release-event", + G_CALLBACK (button_release_event_cb), NULL); + + g_signal_connect(da, "motion-notify-event", + G_CALLBACK (motion_notify_event_cb), NULL); + + gtk_widget_set_events (da, gtk_widget_get_events (da) + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); + + scrollda = gtk_scrolled_window_new(NULL, NULL); + + gtk_container_add(GTK_CONTAINER(scrollda), da); + gtk_widget_show_all(scrollda); + own_page_num = gtk_notebook_append_page(global->notebook, scrollda, gtk_label_new("Receive")); +} + |