diff --git a/gdk/broadway/Makefile.am b/gdk/broadway/Makefile.am index 592322ec7f..d608b6efff 100644 --- a/gdk/broadway/Makefile.am +++ b/gdk/broadway/Makefile.am @@ -17,6 +17,8 @@ LDADDS = $(GDK_DEP_LIBS) noinst_LTLIBRARIES = libbroadway.la libgdk-broadway.la +libexec_PROGRAMS = broadway-server + libgdkinclude_HEADERS = \ gdkbroadway.h @@ -27,6 +29,8 @@ libgdkbroadwayinclude_HEADERS = \ gdkbroadwayvisual.h libbroadway_la_SOURCES = \ + gdkbroadway-server.h \ + gdkbroadway-server.c \ broadway.h \ broadway.c @@ -77,6 +81,11 @@ libgdk_broadway_la_SOURCES = \ libgdk_broadway_la_LIBADD = libbroadway.la +broadway_server_SOURCES = \ + broadway-server.c + +broadway_server_LDADD = libbroadway.la $(GDK_DEP_LIBS) + MAINTAINERCLEANFILES = $(broadway_built_sources) EXTRA_DIST += $(broadway_built_sources) diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c new file mode 100644 index 0000000000..7395abd1ea --- /dev/null +++ b/gdk/broadway/broadway-server.c @@ -0,0 +1,32 @@ +#include + +#include "gdkbroadway-server.h" + +int +main (int argc, char *argv[]) +{ + GdkBroadwayServer *server; + GError *error; + GMainLoop *loop; + + error = NULL; + server = _gdk_broadway_server_new (8080, &error); + if (server == NULL) + { + g_printerr ("%s\n", error->message); + return 1; + } + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + return 0; +} + + +/* TODO: */ + +void +_gdk_broadway_events_got_input (BroadwayInputMsg *message) +{ +} diff --git a/gdk/broadway/broadway.h b/gdk/broadway/broadway.h index e44f332acc..269a59d77e 100644 --- a/gdk/broadway/broadway.h +++ b/gdk/broadway/broadway.h @@ -1,3 +1,6 @@ +#ifndef __BROADWAY_H__ +#define __BROADWAY_H__ + #include #include @@ -78,3 +81,5 @@ void broadway_output_grab_pointer (BroadwayOutput *output, gboolean owner_event); guint32 broadway_output_ungrab_pointer (BroadwayOutput *output); void broadway_output_pong (BroadwayOutput *output); + +#endif /* __BROADWAY_H__ */ diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js index cea91dbccd..acfec428e0 100644 --- a/gdk/broadway/broadway.js +++ b/gdk/broadway/broadway.js @@ -2671,7 +2671,7 @@ function handleKeyDown(e) { // browser behaviors or it has no corresponding keyPress // event, then send it immediately if (!ignoreKeyEvent(ev)) - sendInput("k", [keysym, lastState]); + sendInput("k", [realWindowWithMouse, keysym, lastState]); suppress = true; } @@ -2716,7 +2716,7 @@ function handleKeyPress(e) { // Send the translated keysym if (keysym > 0) - sendInput ("k", [keysym, lastState]); + sendInput ("k", [realWindowWithMouse, keysym, lastState]); // Stop keypress events just in case return cancelEvent(ev); @@ -2735,7 +2735,7 @@ function handleKeyUp(e) { } if (keysym > 0) - sendInput ("K", [keysym, lastState]); + sendInput ("K", [realWindowWithMouse, keysym, lastState]); return cancelEvent(ev); } diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c new file mode 100644 index 0000000000..f0992aaf91 --- /dev/null +++ b/gdk/broadway/gdkbroadway-server.c @@ -0,0 +1,1695 @@ +#include "gdkbroadway-server.h" + +#include "broadway.h" +#include "gdkprivate-broadway.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct BroadwayInput BroadwayInput; + +struct _GdkBroadwayServer { + GObject parent_instance; + + int port; + GSocketService *service; + BroadwayOutput *output; + guint32 id_counter; + guint32 saved_serial; + guint64 last_seen_time; + BroadwayInput *input; + GList *input_messages; + guint process_input_idle; + + GHashTable *id_ht; + GList *toplevels; + + gint32 mouse_in_toplevel_id; + int last_x, last_y; /* in root coords */ + guint32 last_state; + gint32 real_mouse_in_toplevel_id; /* Not affected by grabs */ + + /* Explicit pointer grabs: */ + gint32 pointer_grab_window_id; /* -1 => none */ + guint32 pointer_grab_time; + gboolean pointer_grab_owner_events; + + /* Future data, from the currently queued events */ + int future_root_x; + int future_root_y; + guint32 future_state; + int future_mouse_in_toplevel; +}; + +struct _GdkBroadwayServerClass +{ + GObjectClass parent_class; +}; + +typedef struct HttpRequest { + GdkBroadwayServer *server; + GSocketConnection *connection; + GDataInputStream *data; + GString *request; +} HttpRequest; + +struct BroadwayInput { + GdkBroadwayServer *server; + GSocketConnection *connection; + GByteArray *buffer; + GSource *source; + gboolean seen_time; + gint64 time_base; + gboolean proto_v7_plus; + gboolean binary; +}; + +typedef struct { + gint32 id; + gint32 x; + gint32 y; + gint32 width; + gint32 height; + gboolean is_temp; + gboolean last_synced; + gboolean visible; + gint32 transient_for; + + cairo_surface_t *last_surface; +} BroadwayWindow; + +static void _gdk_broadway_server_resync_windows (GdkBroadwayServer *server); + +G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT) + +static void +gdk_broadway_server_init (GdkBroadwayServer *server) +{ + BroadwayWindow *root; + + server->service = g_socket_service_new (); + server->pointer_grab_window_id = -1; + server->saved_serial = 1; + server->last_seen_time = 1; + server->id_ht = g_hash_table_new (NULL, NULL); + server->id_counter = 0; + + root = g_new0 (BroadwayWindow, 1); + root->id = server->id_counter++; + root->width = 1024; + root->height = 768; + root->visible = TRUE; + + g_hash_table_insert (server->id_ht, + GINT_TO_POINTER (root->id), + root); +} + +static void +gdk_broadway_server_finalize (GObject *object) +{ + G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object); +} + +static void +gdk_broadway_server_class_init (GdkBroadwayServerClass * class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = gdk_broadway_server_finalize; +} + +static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary); + +static void +http_request_free (HttpRequest *request) +{ + g_object_unref (request->connection); + g_object_unref (request->data); + g_string_free (request->request, TRUE); + g_free (request); +} + +static void +broadway_input_free (BroadwayInput *input) +{ + g_object_unref (input->connection); + g_byte_array_free (input->buffer, FALSE); + g_source_destroy (input->source); + g_free (input); +} + +static void +update_event_state (GdkBroadwayServer *server, + BroadwayInputMsg *message) +{ + switch (message->base.type) { + case 'e': /* Enter */ + server->last_x = message->pointer.root_x; + server->last_y = message->pointer.root_y; + server->last_state = message->pointer.state; + server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; + + /* TODO: Unset when it dies */ + server->mouse_in_toplevel_id = message->pointer.event_window_id; + break; + case 'l': /* Leave */ + server->last_x = message->pointer.root_x; + server->last_y = message->pointer.root_y; + server->last_state = message->pointer.state; + server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; + + server->mouse_in_toplevel_id = 0; + break; + case 'm': /* Mouse move */ + server->last_x = message->pointer.root_x; + server->last_y = message->pointer.root_y; + server->last_state = message->pointer.state; + server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; + break; + case 'b': + case 'B': + server->last_x = message->pointer.root_x; + server->last_y = message->pointer.root_y; + server->last_state = message->pointer.state; + server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; + break; + case 's': + server->last_x = message->pointer.root_x; + server->last_y = message->pointer.root_y; + server->last_state = message->pointer.state; + server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; + break; + case 'k': + case 'K': + server->last_state = message->key.state; + break; + case 'g': + case 'u': + break; + case 'w': + break; + case 'W': + break; + case 'd': + break; + + default: + g_printerr ("update_event_state - Unknown input command %c\n", message->base.type); + break; + } +} + +gboolean +_gdk_broadway_server_lookahead_event (GdkBroadwayServer *server, + const char *types) +{ + BroadwayInputMsg *message; + GList *l; + + for (l = server->input_messages; l != NULL; l = l->next) + { + message = l->data; + if (strchr (types, message->base.type) != NULL) + return TRUE; + } + + return FALSE; +} + +static void +process_input_messages (GdkBroadwayServer *server) +{ + BroadwayInputMsg *message; + + while (server->input_messages) + { + message = server->input_messages->data; + server->input_messages = + g_list_delete_link (server->input_messages, + server->input_messages); + + + update_event_state (server, message); + _gdk_broadway_events_got_input (message); + g_free (message); + } +} + +static char * +parse_pointer_data (char *p, BroadwayInputPointerMsg *data) +{ + data->mouse_window_id = strtol (p, &p, 10); + p++; /* Skip , */ + data->event_window_id = strtol (p, &p, 10); + p++; /* Skip , */ + data->root_x = strtol (p, &p, 10); + p++; /* Skip , */ + data->root_y = strtol (p, &p, 10); + p++; /* Skip , */ + data->win_x = strtol (p, &p, 10); + p++; /* Skip , */ + data->win_y = strtol (p, &p, 10); + p++; /* Skip , */ + data->state = strtol (p, &p, 10); + + return p; +} + +static void +update_future_pointer_info (GdkBroadwayServer *server, BroadwayInputPointerMsg *data) +{ + server->future_root_x = data->root_x; + server->future_root_y = data->root_y; + server->future_state = data->state; + server->future_mouse_in_toplevel = data->mouse_window_id; +} + +static void +parse_input_message (BroadwayInput *input, const char *message) +{ + GdkBroadwayServer *server = input->server; + BroadwayInputMsg msg; + char *p; + gint64 time_; + + p = (char *)message; + msg.base.type = *p++; + msg.base.serial = (guint32)strtol (p, &p, 10); + p++; /* Skip , */ + time_ = strtol(p, &p, 10); + p++; /* Skip , */ + + if (time_ == 0) { + time_ = server->last_seen_time; + } else { + if (!input->seen_time) { + input->seen_time = TRUE; + /* Calculate time base so that any following times are normalized to start + 5 seconds after last_seen_time, to avoid issues that could appear when + a long hiatus due to a reconnect seems to be instant */ + input->time_base = time_ - (server->last_seen_time + 5000); + } + time_ = time_ - input->time_base; + } + + server->last_seen_time = time_; + + msg.base.time = time_; + + switch (msg.base.type) { + case 'e': /* Enter */ + case 'l': /* Leave */ + p = parse_pointer_data (p, &msg.pointer); + update_future_pointer_info (server, &msg.pointer); + p++; /* Skip , */ + msg.crossing.mode = strtol(p, &p, 10); + break; + + case 'm': /* Mouse move */ + p = parse_pointer_data (p, &msg.pointer); + update_future_pointer_info (server, &msg.pointer); + break; + + case 'b': + case 'B': + p = parse_pointer_data (p, &msg.pointer); + update_future_pointer_info (server, &msg.pointer); + p++; /* Skip , */ + msg.button.button = strtol(p, &p, 10); + break; + + case 's': + p = parse_pointer_data (p, &msg.pointer); + update_future_pointer_info (server, &msg.pointer); + p++; /* Skip , */ + msg.scroll.dir = strtol(p, &p, 10); + break; + + case 'k': + case 'K': + msg.key.mouse_window_id = strtol(p, &p, 10); + p++; /* Skip , */ + msg.key.key = strtol(p, &p, 10); + p++; /* Skip , */ + msg.key.state = strtol(p, &p, 10); + break; + + case 'g': + case 'u': + msg.grab_reply.res = strtol(p, &p, 10); + break; + + case 'w': + msg.configure_notify.id = strtol(p, &p, 10); + p++; /* Skip , */ + msg.configure_notify.x = strtol (p, &p, 10); + p++; /* Skip , */ + msg.configure_notify.y = strtol (p, &p, 10); + p++; /* Skip , */ + msg.configure_notify.width = strtol (p, &p, 10); + p++; /* Skip , */ + msg.configure_notify.height = strtol (p, &p, 10); + break; + + case 'W': + msg.delete_notify.id = strtol(p, &p, 10); + break; + + case 'd': + msg.screen_resize_notify.width = strtol (p, &p, 10); + p++; /* Skip , */ + msg.screen_resize_notify.height = strtol (p, &p, 10); + break; + + default: + g_printerr ("parse_input_message - Unknown input command %c (%s)\n", msg.base.type, message); + break; + } + + server->input_messages = g_list_append (server->input_messages, g_memdup (&msg, sizeof (msg))); + +} + +static inline void +hex_dump (guchar *data, gsize len) +{ +#ifdef DEBUG_WEBSOCKETS + gsize i, j; + for (j = 0; j < len + 15; j += 16) + { + fprintf (stderr, "0x%.4x ", j); + for (i = 0; i < 16; i++) + { + if ((j + i) < len) + fprintf (stderr, "%.2x ", data[j+i]); + else + fprintf (stderr, " "); + if (i == 8) + fprintf (stderr, " "); + } + fprintf (stderr, " | "); + + for (i = 0; i < 16; i++) + if ((j + i) < len && g_ascii_isalnum(data[j+i])) + fprintf (stderr, "%c", data[j+i]); + else + fprintf (stderr, "."); + fprintf (stderr, "\n"); + } +#endif +} + +static void +parse_input (BroadwayInput *input) +{ + GdkBroadwayServer *server = input->server; + + if (!input->buffer->len) + return; + + if (input->proto_v7_plus) + { + hex_dump (input->buffer->data, input->buffer->len); + + while (input->buffer->len > 2) + { + gsize len, payload_len; + BroadwayWSOpCode code; + gboolean is_mask, fin; + guchar *buf, *data, *mask; + + buf = input->buffer->data; + len = input->buffer->len; + +#ifdef DEBUG_WEBSOCKETS + g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]); +#endif + + fin = buf[0] & 0x80; + code = buf[0] & 0x0f; + payload_len = buf[1] & 0x7f; + is_mask = buf[1] & 0x80; + data = buf + 2; + + if (payload_len > 125) + { + if (len < 4) + return; + payload_len = GUINT16_FROM_BE( *(guint16 *) data ); + data += 2; + } + else if (payload_len > 126) + { + if (len < 10) + return; + payload_len = GUINT64_FROM_BE( *(guint64 *) data ); + data += 8; + } + + mask = NULL; + if (is_mask) + { + if (data - buf + 4 > len) + return; + mask = data; + data += 4; + } + + if (data - buf + payload_len > len) + return; /* wait to accumulate more */ + + if (is_mask) + { + gsize i; + for (i = 0; i < payload_len; i++) + data[i] ^= mask[i%4]; + } + + switch (code) { + case BROADWAY_WS_CNX_CLOSE: + break; /* hang around anyway */ + case BROADWAY_WS_TEXT: + if (!fin) + { +#ifdef DEBUG_WEBSOCKETS + g_warning ("can't yet accept fragmented input"); +#endif + } + else + { + char *terminated = g_strndup((char *)data, payload_len); + parse_input_message (input, terminated); + g_free (terminated); + } + break; + case BROADWAY_WS_CNX_PING: + broadway_output_pong (server->output); + break; + case BROADWAY_WS_CNX_PONG: + break; /* we never send pings, but tolerate pongs */ + case BROADWAY_WS_BINARY: + case BROADWAY_WS_CONTINUATION: + default: + { + g_warning ("fragmented or unknown input code 0x%2x with fin set", code); + break; + } + } + + g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len); + } + } + else /* old style protocol */ + { + char *buf, *ptr; + gsize len; + + buf = (char *)input->buffer->data; + len = input->buffer->len; + + if (buf[0] != 0) + { + server->input = NULL; + broadway_input_free (input); + return; + } + + while ((ptr = memchr (buf, 0xff, len)) != NULL) + { + *ptr = 0; + ptr++; + + parse_input_message (input, buf + 1); + + len -= ptr - buf; + buf = ptr; + + if (len > 0 && buf[0] != 0) + { + server->input = NULL; + broadway_input_free (input); + break; + } + } + g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data); + } +} + + +static gboolean +process_input_idle_cb (GdkBroadwayServer *server) +{ + server->process_input_idle = 0; + process_input_messages (server); + return G_SOURCE_REMOVE; +} + +static void +queue_process_input_at_idle (GdkBroadwayServer *server) +{ + if (server->process_input_idle == 0) + server->process_input_idle = + g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL); +} + +static void +_gdk_broadway_server_read_all_input_nonblocking (GdkBroadwayServer *server) +{ + GInputStream *in; + gssize res; + guint8 buffer[1024]; + GError *error; + BroadwayInput *input; + + if (server->input == NULL) + return; + + input = server->input; + + in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); + + error = NULL; + res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in), + buffer, sizeof (buffer), NULL, &error); + + if (res <= 0) + { + if (res < 0 && + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + { + g_error_free (error); + return; + } + + server->input = NULL; + broadway_input_free (input); + if (res < 0) + { + g_print ("input error %s\n", error->message); + g_error_free (error); + } + return; + } + + g_byte_array_append (input->buffer, buffer, res); + + parse_input (input); +} + +static void +_gdk_broadway_server_consume_all_input (GdkBroadwayServer *server) +{ + _gdk_broadway_server_read_all_input_nonblocking (server); + + /* Since we're parsing input but not processing the resulting messages + we might not get a readable callback on the stream, so queue an idle to + process the messages */ + queue_process_input_at_idle (server); +} + + +static gboolean +input_data_cb (GObject *stream, + BroadwayInput *input) +{ + GdkBroadwayServer *server = input->server; + + _gdk_broadway_server_read_all_input_nonblocking (server); + + process_input_messages (server); + + return TRUE; +} + +gulong +_gdk_broadway_server_get_next_serial (GdkBroadwayServer *server) +{ + if (server->output) + return broadway_output_get_next_serial (server->output); + + return server->saved_serial; +} + +void +_gdk_broadway_server_flush (GdkBroadwayServer *server) +{ + if (server->output && + !broadway_output_flush (server->output)) + { + server->saved_serial = broadway_output_get_next_serial (server->output); + broadway_output_free (server->output); + server->output = NULL; + } +} + +void +_gdk_broadway_server_sync (GdkBroadwayServer *server) +{ + _gdk_broadway_server_flush (server); +} + + +/* TODO: This is not used atm, is it needed? */ +/* Note: This may be called while handling a message (i.e. sorta recursively) */ +BroadwayInputMsg * +_gdk_broadway_server_block_for_input (GdkBroadwayServer *server, char op, + guint32 serial, gboolean remove_message) +{ + BroadwayInputMsg *message; + gssize res; + guint8 buffer[1024]; + BroadwayInput *input; + GInputStream *in; + GList *l; + + _gdk_broadway_server_flush (server); + + if (server->input == NULL) + return NULL; + + input = server->input; + + while (TRUE) { + /* Check for existing reply in queue */ + + for (l = server->input_messages; l != NULL; l = l->next) + { + message = l->data; + + if (message->base.type == op) + { + if (message->base.serial == serial) + { + if (remove_message) + server->input_messages = + g_list_delete_link (server->input_messages, l); + return message; + } + } + } + + /* Not found, read more, blocking */ + + in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); + res = g_input_stream_read (in, buffer, sizeof (buffer), NULL, NULL); + if (res <= 0) + return NULL; + g_byte_array_append (input->buffer, buffer, res); + + parse_input (input); + + /* Since we're parsing input but not processing the resulting messages + we might not get a readable callback on the stream, so queue an idle to + process the messages */ + queue_process_input_at_idle (server); + } +} + +static char * +parse_line (char *line, char *key) +{ + char *p; + + if (!g_str_has_prefix (line, key)) + return NULL; + p = line + strlen (key); + if (*p != ':') + return NULL; + p++; + /* Skip optional initial space */ + if (*p == ' ') + p++; + return p; +} +static void +send_error (HttpRequest *request, + int error_code, + const char *reason) +{ + char *res; + + res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n" + "%d %s" + "%s", + error_code, reason, + error_code, reason, + reason); + /* TODO: This should really be async */ + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + res, strlen (res), NULL, NULL, NULL); + g_free (res); + http_request_free (request); +} + +/* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */ +#define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +/* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */ +static gchar * +generate_handshake_response_wsietf_v7 (const gchar *key) +{ + gsize digest_len = 20; + guchar digest[digest_len]; + GChecksum *checksum; + + checksum = g_checksum_new (G_CHECKSUM_SHA1); + if (!checksum) + return NULL; + + g_checksum_update (checksum, (guchar *)key, -1); + g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1); + + g_checksum_get_digest (checksum, digest, &digest_len); + g_checksum_free (checksum); + + g_assert (digest_len == 20); + + return g_base64_encode (digest, digest_len); +} + +static void +start_input (HttpRequest *request, gboolean binary) +{ + char **lines; + char *p; + int num_key1, num_key2; + guint64 key1, key2; + int num_space; + int i; + guint8 challenge[16]; + char *res; + gsize len; + GChecksum *checksum; + char *origin, *host; + GdkBroadwayServer *server; + BroadwayInput *input; + const void *data_buffer; + gsize data_buffer_size; + GInputStream *in; + char *key_v7; + gboolean proto_v7_plus; + + server = GDK_BROADWAY_SERVER (request->server); + + if (server->input != NULL) + { + send_error (request, 409, "Input already handled"); + return; + } + +#ifdef DEBUG_WEBSOCKETS + g_print ("incoming request:\n%s\n", request->request->str); +#endif + lines = g_strsplit (request->request->str, "\n", 0); + + num_key1 = 0; + num_key2 = 0; + key1 = 0; + key2 = 0; + key_v7 = NULL; + origin = NULL; + host = NULL; + for (i = 0; lines[i] != NULL; i++) + { + if ((p = parse_line (lines[i], "Sec-WebSocket-Key1"))) + { + num_space = 0; + while (*p != 0) + { + if (g_ascii_isdigit (*p)) + key1 = key1 * 10 + g_ascii_digit_value (*p); + else if (*p == ' ') + num_space++; + + p++; + } + key1 /= num_space; + num_key1++; + } + else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2"))) + { + num_space = 0; + while (*p != 0) + { + if (g_ascii_isdigit (*p)) + key2 = key2 * 10 + g_ascii_digit_value (*p); + else if (*p == ' ') + num_space++; + + p++; + } + key2 /= num_space; + num_key2++; + } + else if ((p = parse_line (lines[i], "Sec-WebSocket-Key"))) + { + key_v7 = p; + } + else if ((p = parse_line (lines[i], "Origin"))) + { + origin = p; + } + else if ((p = parse_line (lines[i], "Host"))) + { + host = p; + } + else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin"))) + { + origin = p; + } + } + + if (origin == NULL || host == NULL) + { + g_strfreev (lines); + send_error (request, 400, "Bad websocket request"); + return; + } + + if (key_v7 != NULL) + { + char* accept = generate_handshake_response_wsietf_v7 (key_v7); + res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "Sec-WebSocket-Origin: %s\r\n" + "Sec-WebSocket-Location: ws://%s/socket\r\n" + "Sec-WebSocket-Protocol: broadway\r\n" + "\r\n", accept, origin, host); + g_free (accept); + +#ifdef DEBUG_WEBSOCKETS + g_print ("v7 proto response:\n%s", res); +#endif + + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + res, strlen (res), NULL, NULL, NULL); + g_free (res); + proto_v7_plus = TRUE; + } + else + { + if (num_key1 != 1 || num_key2 != 1) + { + g_strfreev (lines); + send_error (request, 400, "Bad websocket request"); + return; + } + + challenge[0] = (key1 >> 24) & 0xff; + challenge[1] = (key1 >> 16) & 0xff; + challenge[2] = (key1 >> 8) & 0xff; + challenge[3] = (key1 >> 0) & 0xff; + challenge[4] = (key2 >> 24) & 0xff; + challenge[5] = (key2 >> 16) & 0xff; + challenge[6] = (key2 >> 8) & 0xff; + challenge[7] = (key2 >> 0) & 0xff; + + if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL)) + { + g_strfreev (lines); + send_error (request, 400, "Bad websocket request"); + return; + } + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, challenge, 16); + len = 16; + g_checksum_get_digest (checksum, challenge, &len); + g_checksum_free (checksum); + + res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Origin: %s\r\n" + "Sec-WebSocket-Location: ws://%s/socket\r\n" + "Sec-WebSocket-Protocol: broadway\r\n" + "\r\n", + origin, host); + +#ifdef DEBUG_WEBSOCKETS + g_print ("legacy response:\n%s", res); +#endif + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + res, strlen (res), NULL, NULL, NULL); + g_free (res); + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + challenge, 16, NULL, NULL, NULL); + proto_v7_plus = FALSE; + } + + input = g_new0 (BroadwayInput, 1); + + input->server = request->server; + input->connection = g_object_ref (request->connection); + input->proto_v7_plus = proto_v7_plus; + input->binary = binary; + + data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size); + input->buffer = g_byte_array_sized_new (data_buffer_size); + g_byte_array_append (input->buffer, data_buffer, data_buffer_size); + + server->input = input; + + start_output (request, proto_v7_plus, binary); + + /* This will free and close the data input stream, but we got all the buffered content already */ + http_request_free (request); + + in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); + input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL); + g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL); + g_source_attach (input->source, NULL); + + /* Process any data in the pipe already */ + parse_input (input); + process_input_messages (server); + + g_strfreev (lines); +} + +static void +start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary) +{ + GSocket *socket; + GdkBroadwayServer *server; + int flag = 1; + + socket = g_socket_connection_get_socket (request->connection); + setsockopt(g_socket_get_fd (socket), IPPROTO_TCP, + TCP_NODELAY, (char *) &flag, sizeof(int)); + + server = GDK_BROADWAY_SERVER (request->server); + + if (server->output) + { + server->saved_serial = broadway_output_get_next_serial (server->output); + broadway_output_free (server->output); + } + + server->output = + broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + server->saved_serial, proto_v7_plus, binary); + + _gdk_broadway_server_resync_windows (server); + + if (server->pointer_grab_window_id != -1) + broadway_output_grab_pointer (server->output, + server->pointer_grab_window_id, + server->pointer_grab_owner_events); +} + +static void +send_data (HttpRequest *request, + const char *mimetype, + const char *data, gsize len) +{ + char *res; + + res = g_strdup_printf ("HTTP/1.0 200 OK\r\n" + "Content-Type: %s\r\n" + "Content-Length: %"G_GSIZE_FORMAT"\r\n" + "\r\n", + mimetype, len); + /* TODO: This should really be async */ + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + res, strlen (res), NULL, NULL, NULL); + g_free (res); + g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + data, len, NULL, NULL, NULL); + http_request_free (request); +} + +#include "clienthtml.h" +#include "broadwayjs.h" + +static void +got_request (HttpRequest *request) +{ + char *start, *escaped, *tmp, *version, *query; + + if (!g_str_has_prefix (request->request->str, "GET ")) + { + send_error (request, 501, "Only GET implemented"); + return; + } + + start = request->request->str + 4; /* Skip "GET " */ + + while (*start == ' ') + start++; + + for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) + ; + escaped = g_strndup (start, tmp - start); + version = NULL; + if (*tmp == ' ') + { + start = tmp; + while (*start == ' ') + start++; + for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) + ; + version = g_strndup (start, tmp - start); + } + + query = strchr (escaped, '?'); + if (query) + *query = 0; + + if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0) + send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1); + else if (strcmp (escaped, "/broadway.js") == 0) + send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1); + else if (strcmp (escaped, "/socket") == 0) + start_input (request, FALSE); + else if (strcmp (escaped, "/socket-bin") == 0) + start_input (request, TRUE); + else + send_error (request, 404, "File not found"); + + g_free (escaped); + g_free (version); +} + +static void +got_http_request_line (GInputStream *stream, + GAsyncResult *result, + HttpRequest *request) +{ + char *line; + + line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL); + if (line == NULL) + { + http_request_free (request); + g_printerr ("Error reading request lines\n"); + return; + } + if (strlen (line) == 0) + got_request (request); + else + { + /* Protect against overflow in request length */ + if (request->request->len > 1024 * 5) + { + send_error (request, 400, "Request too long"); + } + else + { + g_string_append_printf (request->request, "%s\n", line); + g_data_input_stream_read_line_async (request->data, 0, NULL, + (GAsyncReadyCallback)got_http_request_line, request); + } + } + g_free (line); +} + +static gboolean +handle_incoming_connection (GSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + HttpRequest *request; + GInputStream *in; + + request = g_new0 (HttpRequest, 1); + request->connection = g_object_ref (connection); + request->server = GDK_BROADWAY_SERVER (source_object); + request->request = g_string_new (""); + + in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); + + request->data = g_data_input_stream_new (in); + g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE); + /* Be tolerant of input */ + g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY); + + g_data_input_stream_read_line_async (request->data, 0, NULL, + (GAsyncReadyCallback)got_http_request_line, request); + return TRUE; +} + +GdkBroadwayServer * +_gdk_broadway_server_new (int port, GError **error) +{ + GdkBroadwayServer *server; + + server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL); + server->port = port; + + if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (server->service), + server->port, + G_OBJECT (server), + error)) + { + g_prefix_error (error, "Unable to listen to port %d: ", server->port); + return NULL; + } + + g_signal_connect (server->service, "incoming", + G_CALLBACK (handle_incoming_connection), NULL); + return server; +} + +guint32 +_gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server) +{ + _gdk_broadway_server_consume_all_input (server); + return (guint32) server->last_seen_time; +} + +void +_gdk_broadway_server_query_mouse (GdkBroadwayServer *server, + gint32 *toplevel, + gint32 *root_x, + gint32 *root_y, + guint32 *mask) +{ + if (server->output) + { + _gdk_broadway_server_consume_all_input (server); + if (root_x) + *root_x = server->future_root_x; + if (root_y) + *root_y = server->future_root_y; + if (mask) + *mask = server->future_state; + if (toplevel) + *toplevel = server->future_mouse_in_toplevel; + return; + } + + /* Fallback when unconnected */ + if (root_x) + *root_x = server->last_x; + if (root_y) + *root_y = server->last_y; + if (mask) + *mask = server->last_state; + if (toplevel) + *toplevel = server->mouse_in_toplevel_id; +} + +void +_gdk_broadway_server_destroy_window (GdkBroadwayServer *server, + gint id) +{ + BroadwayWindow *window; + + if (server->mouse_in_toplevel_id == id) + { + /* TODO: Send leave + enter event, update cursors, etc */ + server->mouse_in_toplevel_id = 0; + } + + if (server->pointer_grab_window_id == id) + server->pointer_grab_window_id = -1; + + if (server->output) + broadway_output_destroy_surface (server->output, + id); + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window != NULL) + { + server->toplevels = g_list_remove (server->toplevels, window); + g_hash_table_remove (server->id_ht, + GINT_TO_POINTER (id)); + g_free (window); + } +} + +gboolean +_gdk_broadway_server_window_show (GdkBroadwayServer *server, + gint id) +{ + BroadwayWindow *window; + gboolean sent = FALSE; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return FALSE; + + window->visible = TRUE; + + if (server->output) + { + broadway_output_show_surface (server->output, window->id); + sent = TRUE; + } + + return sent; +} + +gboolean +_gdk_broadway_server_window_hide (GdkBroadwayServer *server, + gint id) +{ + BroadwayWindow *window; + gboolean sent = FALSE; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return FALSE; + + window->visible = FALSE; + + if (server->mouse_in_toplevel_id == id) + { + /* TODO: Send leave + enter event, update cursors, etc */ + server->mouse_in_toplevel_id = 0; + } + + if (server->output) + { + broadway_output_hide_surface (server->output, window->id); + sent = TRUE; + } + return sent; +} + +void +_gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server, + gint id, gint parent) +{ + BroadwayWindow *window; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return; + + window->transient_for = parent; + + if (server->output) + { + broadway_output_set_transient_for (server->output, window->id, window->transient_for); + _gdk_broadway_server_flush (server); + } +} + +gboolean +_gdk_broadway_server_has_client (GdkBroadwayServer *server) +{ + return server->output != NULL; +} + +static void +_cairo_region (cairo_t *cr, + const cairo_region_t *region) +{ + cairo_rectangle_int_t box; + gint n_boxes, i; + + g_return_if_fail (cr != NULL); + g_return_if_fail (region != NULL); + + n_boxes = cairo_region_num_rectangles (region); + + for (i = 0; i < n_boxes; i++) + { + cairo_region_get_rectangle (region, i, &box); + cairo_rectangle (cr, box.x, box.y, box.width, box.height); + } +} + + +static void +copy_region (cairo_surface_t *surface, + cairo_region_t *area, + gint dx, + gint dy) +{ + cairo_t *cr; + + cr = cairo_create (surface); + + _cairo_region (cr, area); + cairo_clip (cr); + + /* NB: This is a self-copy and Cairo doesn't support that yet. + * So we do a litle trick. + */ + cairo_push_group (cr); + + cairo_set_source_surface (cr, surface, dx, dy); + cairo_paint (cr); + + cairo_pop_group_to_source (cr); + cairo_paint (cr); + + cairo_destroy (cr); +} + +gboolean +_gdk_broadway_server_window_translate (GdkBroadwayServer *server, + gint id, + cairo_region_t *area, + gint dx, + gint dy) +{ + BroadwayWindow *window; + gboolean sent = FALSE; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return FALSE; + + if (window->last_synced && + server->output) + { + BroadwayRect *rects; + cairo_rectangle_int_t rect; + int i, n_rects; + + copy_region (window->last_surface, area, dx, dy); + n_rects = cairo_region_num_rectangles (area); + rects = g_new (BroadwayRect, n_rects); + for (i = 0; i < n_rects; i++) + { + cairo_region_get_rectangle (area, i, &rect); + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + broadway_output_copy_rectangles (server->output, + window->id, + rects, n_rects, dx, dy); + g_free (rects); + sent = TRUE; + } + + return sent; +} + +static void +diff_surfaces (cairo_surface_t *surface, + cairo_surface_t *old_surface) +{ + guint8 *data, *old_data; + guint32 *line, *old_line; + int w, h, stride, old_stride; + int x, y; + + data = cairo_image_surface_get_data (surface); + old_data = cairo_image_surface_get_data (old_surface); + + w = cairo_image_surface_get_width (surface); + h = cairo_image_surface_get_height (surface); + + stride = cairo_image_surface_get_stride (surface); + old_stride = cairo_image_surface_get_stride (old_surface); + + for (y = 0; y < h; y++) + { + line = (guint32 *)data; + old_line = (guint32 *)old_data; + + for (x = 0; x < w; x++) + { + if ((*line & 0xffffff) == (*old_line & 0xffffff)) + *old_line = 0; + else + *old_line = *line | 0xff000000; + line ++; + old_line ++; + } + + data += stride; + old_data += old_stride; + } +} + +void +_gdk_broadway_server_window_update (GdkBroadwayServer *server, + gint id, + cairo_surface_t *surface) +{ + cairo_t *cr; + BroadwayWindow *window; + + if (surface == NULL) + return; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return; + + if (window->last_surface == NULL) + window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + window->width, + window->height); + + if (server->output != NULL) + { + if (window->last_synced) + { + diff_surfaces (surface, + window->last_surface); + broadway_output_put_rgba (server->output, window->id, 0, 0, + cairo_image_surface_get_width (window->last_surface), + cairo_image_surface_get_height (window->last_surface), + cairo_image_surface_get_stride (window->last_surface), + cairo_image_surface_get_data (window->last_surface)); + } + else + { + window->last_synced = TRUE; + broadway_output_put_rgb (server->output, window->id, 0, 0, + cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface), + cairo_image_surface_get_stride (surface), + cairo_image_surface_get_data (surface)); + } + + broadway_output_surface_flush (server->output, window->id); + } + + cr = cairo_create (window->last_surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); +} + +gboolean +_gdk_broadway_server_window_move_resize (GdkBroadwayServer *server, + gint id, + int x, + int y, + int width, + int height) +{ + BroadwayWindow *window; + gboolean with_move, with_resize; + gboolean sent = FALSE; + cairo_t *cr; + + window = g_hash_table_lookup (server->id_ht, + GINT_TO_POINTER (id)); + if (window == NULL) + return FALSE; + + with_move = x != window->x || y != window->y; + with_resize = width != window->width || height != window->height; + window->x = x; + window->y = y; + window->width = width; + window->height = height; + + if (with_resize && window->last_surface != NULL) + { + cairo_surface_t *old; + + old = window->last_surface; + + window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + width, height); + + + cr = cairo_create (window->last_surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, old, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); + + cairo_surface_destroy (old); + } + + if (server->output != NULL) + { + broadway_output_move_resize_surface (server->output, + window->id, + with_move, window->x, window->y, + with_resize, window->width, window->height); + sent = TRUE; + } + + return sent; +} + +GdkGrabStatus +_gdk_broadway_server_grab_pointer (GdkBroadwayServer *server, + gint id, + gboolean owner_events, + guint32 event_mask, + guint32 time_) +{ + if (server->pointer_grab_window_id != -1 && + time_ != 0 && server->pointer_grab_time > time_) + return GDK_GRAB_ALREADY_GRABBED; + + if (time_ == 0) + time_ = server->last_seen_time; + + server->pointer_grab_window_id = id; + server->pointer_grab_owner_events = owner_events; + server->pointer_grab_time = time_; + + if (server->output) + { + broadway_output_grab_pointer (server->output, + id, + owner_events); + _gdk_broadway_server_flush (server); + } + + /* TODO: What about toplevel grab events if we're not connected? */ + + return GDK_GRAB_SUCCESS; +} + +guint32 +_gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server, + guint32 time_) +{ + guint32 serial; + + if (server->pointer_grab_window_id != -1 && + time_ != 0 && server->pointer_grab_time > time_) + return 0; + + /* TODO: What about toplevel grab events if we're not connected? */ + + if (server->output) + { + serial = broadway_output_ungrab_pointer (server->output); + _gdk_broadway_server_flush (server); + } + else + { + serial = server->saved_serial; + } + + server->pointer_grab_window_id = -1; + + return serial; +} + +guint32 +_gdk_broadway_server_new_window (GdkBroadwayServer *server, + int x, + int y, + int width, + int height, + gboolean is_temp) +{ + BroadwayWindow *window; + + window = g_new0 (BroadwayWindow, 1); + window->id = server->id_counter++; + window->x = x; + window->y = y; + window->width = width; + window->height = height; + window->is_temp = is_temp; + + g_hash_table_insert (server->id_ht, + GINT_TO_POINTER (window->id), + window); + + server->toplevels = g_list_prepend (server->toplevels, window); + + if (server->output) + broadway_output_new_surface (server->output, + window->id, + window->x, + window->y, + window->width, + window->height, + window->is_temp); + + return window->id; +} + +static void +_gdk_broadway_server_resync_windows (GdkBroadwayServer *server) +{ + GList *l; + + if (server->output == NULL) + return; + + /* First create all windows */ + for (l = server->toplevels; l != NULL; l = l->next) + { + BroadwayWindow *window = l->data; + + if (window->id == 0) + continue; /* Skip root */ + + window->last_synced = FALSE; + broadway_output_new_surface (server->output, + window->id, + window->x, + window->y, + window->width, + window->height, + window->is_temp); + } + + /* Then do everything that may reference other windows */ + for (l = server->toplevels; l != NULL; l = l->next) + { + BroadwayWindow *window = l->data; + + if (window->id == 0) + continue; /* Skip root */ + + if (window->transient_for != -1) + broadway_output_set_transient_for (server->output, window->id, window->transient_for); + if (window->visible) + { + broadway_output_show_surface (server->output, window->id); + + if (window->last_surface != NULL) + { + window->last_synced = TRUE; + broadway_output_put_rgb (server->output, window->id, 0, 0, + cairo_image_surface_get_width (window->last_surface), + cairo_image_surface_get_height (window->last_surface), + cairo_image_surface_get_stride (window->last_surface), + cairo_image_surface_get_data (window->last_surface)); + } + } + } + + _gdk_broadway_server_flush (server); +} diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h new file mode 100644 index 0000000000..e9f77425da --- /dev/null +++ b/gdk/broadway/gdkbroadway-server.h @@ -0,0 +1,145 @@ +#ifndef __GDK_BROADWAY_SERVER__ +#define __GDK_BROADWAY_SERVER__ + +#include + +typedef struct _GdkBroadwayServer GdkBroadwayServer; +typedef struct _GdkBroadwayServerClass GdkBroadwayServerClass; + +#define GDK_TYPE_BROADWAY_SERVER (gdk_broadway_server_get_type()) +#define GDK_BROADWAY_SERVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServer)) +#define GDK_BROADWAY_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass)) +#define GDK_IS_BROADWAY_SERVER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_BROADWAY_SERVER)) +#define GDK_IS_BROADWAY_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SERVER)) +#define GDK_BROADWAY_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass)) + +typedef struct { + guint8 type; + guint32 serial; + guint64 time; +} BroadwayInputBaseMsg; + +typedef struct { + BroadwayInputBaseMsg base; + guint32 mouse_window_id; /* The real window, not taking grabs into account */ + guint32 event_window_id; + gint32 root_x; + gint32 root_y; + gint32 win_x; + gint32 win_y; + guint32 state; +} BroadwayInputPointerMsg; + +typedef struct { + BroadwayInputPointerMsg pointer; + guint32 mode; +} BroadwayInputCrossingMsg; + +typedef struct { + BroadwayInputPointerMsg pointer; + guint32 button; +} BroadwayInputButtonMsg; + +typedef struct { + BroadwayInputPointerMsg pointer; + gint32 dir; +} BroadwayInputScrollMsg; + +typedef struct { + BroadwayInputBaseMsg base; + guint32 mouse_window_id; /* The real window, not taking grabs into account */ + guint32 state; + gint32 key; +} BroadwayInputKeyMsg; + +typedef struct { + BroadwayInputBaseMsg base; + gint32 res; +} BroadwayInputGrabReply; + +typedef struct { + BroadwayInputBaseMsg base; + gint32 id; + gint32 x; + gint32 y; + gint32 width; + gint32 height; +} BroadwayInputConfigureNotify; + +typedef struct { + BroadwayInputBaseMsg base; + gint32 width; + gint32 height; +} BroadwayInputScreenResizeNotify; + +typedef struct { + BroadwayInputBaseMsg base; + gint32 id; +} BroadwayInputDeleteNotify; + +typedef union { + BroadwayInputBaseMsg base; + BroadwayInputPointerMsg pointer; + BroadwayInputCrossingMsg crossing; + BroadwayInputButtonMsg button; + BroadwayInputScrollMsg scroll; + BroadwayInputKeyMsg key; + BroadwayInputGrabReply grab_reply; + BroadwayInputConfigureNotify configure_notify; + BroadwayInputDeleteNotify delete_notify; + BroadwayInputScreenResizeNotify screen_resize_notify; +} BroadwayInputMsg; + +GdkBroadwayServer *_gdk_broadway_server_new (int port, + GError **error); +gboolean _gdk_broadway_server_has_client (GdkBroadwayServer *server); +void _gdk_broadway_server_flush (GdkBroadwayServer *server); +void _gdk_broadway_server_sync (GdkBroadwayServer *server); +gulong _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server); +guint32 _gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server); +gboolean _gdk_broadway_server_lookahead_event (GdkBroadwayServer *server, + const char *types); +void _gdk_broadway_server_query_mouse (GdkBroadwayServer *server, + gint *toplevel, + gint *root_x, + gint *root_y, + guint32 *mask); +GdkGrabStatus _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server, + gint id, + gboolean owner_events, + guint32 event_mask, + guint32 time_); +guint32 _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server, + guint32 time_); +gint32 _gdk_broadway_server_get_mouse_toplevel (GdkBroadwayServer *server); +guint32 _gdk_broadway_server_new_window (GdkBroadwayServer *server, + int x, + int y, + int width, + int height, + gboolean is_temp); +void _gdk_broadway_server_destroy_window (GdkBroadwayServer *server, + gint id); +gboolean _gdk_broadway_server_window_show (GdkBroadwayServer *server, + gint id); +gboolean _gdk_broadway_server_window_hide (GdkBroadwayServer *server, + gint id); +void _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server, + gint id, + gint parent); +gboolean _gdk_broadway_server_window_translate (GdkBroadwayServer *server, + gint id, + cairo_region_t *area, + gint dx, + gint dy); +void _gdk_broadway_server_window_update (GdkBroadwayServer *server, + gint id, + cairo_surface_t *surface); +gboolean _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server, + gint id, + int x, + int y, + int width, + int height); + +#endif /* __GDK_BROADWAY_SERVER__ */ diff --git a/gdk/broadway/gdkdevice-broadway.c b/gdk/broadway/gdkdevice-broadway.c index 6af72704f9..40e4a2fcce 100644 --- a/gdk/broadway/gdkdevice-broadway.c +++ b/gdk/broadway/gdkdevice-broadway.c @@ -156,7 +156,10 @@ gdk_broadway_device_query_state (GdkDevice *device, GdkDisplay *display; GdkBroadwayDisplay *broadway_display; GdkScreen *screen; - gint device_root_x, device_root_y; + gint32 device_root_x, device_root_y; + gint32 mouse_toplevel_id; + GdkWindow *mouse_toplevel; + guint32 mask32; if (gdk_device_get_source (device) != GDK_SOURCE_MOUSE) return; @@ -173,36 +176,12 @@ gdk_broadway_device_query_state (GdkDevice *device, *root_window = gdk_screen_get_root_window (screen); } - if (broadway_display->output) - { - _gdk_broadway_display_consume_all_input (display); - if (root_x) - *root_x = broadway_display->future_root_x; - if (root_y) - *root_y = broadway_display->future_root_y; - /* TODO: Should really use future_x/y when we get configure events */ - if (win_x) - *win_x = broadway_display->future_root_x - toplevel->x; - if (win_y) - *win_y = broadway_display->future_root_y - toplevel->y; - if (mask) - *mask = broadway_display->future_state; - if (child_window) - { - if (gdk_window_get_window_type (toplevel) == GDK_WINDOW_ROOT) - *child_window = - g_hash_table_lookup (broadway_display->id_ht, - GINT_TO_POINTER (broadway_display->future_mouse_in_toplevel)); - else - *child_window = toplevel; /* No native children */ - } - return; - } - - /* Fallback when unconnected */ - - device_root_x = broadway_display->last_x; - device_root_y = broadway_display->last_y; + _gdk_broadway_server_query_mouse (broadway_display->server, + &mouse_toplevel_id, + &device_root_x, + &device_root_y, + &mask32); + mouse_toplevel = g_hash_table_lookup (broadway_display->id_ht, GINT_TO_POINTER (mouse_toplevel_id)); if (root_x) *root_x = device_root_x; @@ -213,12 +192,12 @@ gdk_broadway_device_query_state (GdkDevice *device, if (win_y) *win_y = device_root_y - toplevel->y; if (mask) - *mask = broadway_display->last_state; + *mask = mask32; if (child_window) { if (gdk_window_get_window_type (toplevel) == GDK_WINDOW_ROOT) { - *child_window = broadway_display->mouse_in_toplevel; + *child_window = mouse_toplevel; if (*child_window == NULL) *child_window = toplevel; } @@ -236,13 +215,10 @@ void _gdk_broadway_window_grab_check_destroy (GdkWindow *window) { GdkDisplay *display = gdk_window_get_display (window); - GdkBroadwayDisplay *broadway_display; GdkDeviceManager *device_manager; GdkDeviceGrabInfo *grab; GList *devices, *d; - broadway_display = GDK_BROADWAY_DISPLAY (display); - device_manager = gdk_display_get_device_manager (display); /* Get all devices */ @@ -257,8 +233,6 @@ _gdk_broadway_window_grab_check_destroy (GdkWindow *window) { grab->serial_end = grab->serial_start; grab->implicit_ungrab = TRUE; - - broadway_display->pointer_grab_window = NULL; } } @@ -290,29 +264,11 @@ gdk_broadway_device_grab (GdkDevice *device, else { /* Device is a pointer */ - - if (broadway_display->pointer_grab_window != NULL && - time_ != 0 && broadway_display->pointer_grab_time > time_) - return GDK_GRAB_ALREADY_GRABBED; - - if (time_ == 0) - time_ = broadway_display->last_seen_time; - - broadway_display->pointer_grab_window = window; - broadway_display->pointer_grab_owner_events = owner_events; - broadway_display->pointer_grab_time = time_; - - if (broadway_display->output) - { - broadway_output_grab_pointer (broadway_display->output, - GDK_WINDOW_IMPL_BROADWAY (window->impl)->id, - owner_events); - gdk_display_flush (display); - } - - /* TODO: What about toplevel grab events if we're not connected? */ - - return GDK_GRAB_SUCCESS; + return _gdk_broadway_server_grab_pointer (broadway_display->server, + GDK_WINDOW_IMPL_BROADWAY (window->impl)->id, + owner_events, + event_mask, + time_); } } @@ -340,31 +296,17 @@ gdk_broadway_device_ungrab (GdkDevice *device, else { /* Device is a pointer */ + serial = _gdk_broadway_server_ungrab_pointer (broadway_display->server, time_); - if (broadway_display->pointer_grab_window != NULL && - time_ != 0 && broadway_display->pointer_grab_time > time_) - return; - - /* TODO: What about toplevel grab events if we're not connected? */ - - if (broadway_display->output) + if (serial != 0) { - serial = broadway_output_ungrab_pointer (broadway_display->output); - gdk_display_flush (display); + grab = _gdk_display_get_last_device_grab (display, device); + if (grab && + (time_ == GDK_CURRENT_TIME || + grab->time == GDK_CURRENT_TIME || + !TIME_IS_LATER (grab->time, time_))) + grab->serial_end = serial; } - else - { - serial = broadway_display->saved_serial; - } - - grab = _gdk_display_get_last_device_grab (display, device); - if (grab && - (time_ == GDK_CURRENT_TIME || - grab->time == GDK_CURRENT_TIME || - !TIME_IS_LATER (grab->time, time_))) - grab->serial_end = serial; - - broadway_display->pointer_grab_window = NULL; } } @@ -375,7 +317,6 @@ gdk_broadway_device_window_at_position (GdkDevice *device, GdkModifierType *mask, gboolean get_toplevel) { - gboolean res; GdkScreen *screen; GdkWindow *root_window; GdkWindow *window; diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c index 97dfe879cd..ad5d24b6ff 100644 --- a/gdk/broadway/gdkdisplay-broadway.c +++ b/gdk/broadway/gdkdisplay-broadway.c @@ -67,8 +67,6 @@ gdk_event_init (GdkDisplay *display) broadway_display = GDK_BROADWAY_DISPLAY (display); broadway_display->event_source = _gdk_broadway_event_source_new (display); - broadway_display->saved_serial = 1; - broadway_display->last_seen_time = 1; } static void @@ -123,951 +121,16 @@ gdk_broadway_display_init_input (GdkDisplay *display) g_list_free (list); } -typedef struct HttpRequest { - GdkDisplay *display; - GSocketConnection *connection; - GDataInputStream *data; - GString *request; -} HttpRequest; - -static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary); - -static void -http_request_free (HttpRequest *request) -{ - g_object_unref (request->connection); - g_object_unref (request->data); - g_string_free (request->request, TRUE); - g_free (request); -} - -struct BroadwayInput { - GdkDisplay *display; - GSocketConnection *connection; - GByteArray *buffer; - GSource *source; - gboolean seen_time; - gint64 time_base; - gboolean proto_v7_plus; - gboolean binary; -}; - -static void -broadway_input_free (BroadwayInput *input) -{ - g_object_unref (input->connection); - g_byte_array_free (input->buffer, FALSE); - g_source_destroy (input->source); - g_free (input); -} - -static void -process_input_messages (GdkBroadwayDisplay *broadway_display) -{ - BroadwayInputMsg *message; - - while (broadway_display->input_messages) - { - message = broadway_display->input_messages->data; - broadway_display->input_messages = - g_list_delete_link (broadway_display->input_messages, - broadway_display->input_messages); - - _gdk_broadway_events_got_input (GDK_DISPLAY (broadway_display), message); - g_free (message); - } -} - -static char * -parse_pointer_data (char *p, BroadwayInputPointerMsg *data) -{ - data->mouse_window_id = strtol (p, &p, 10); - p++; /* Skip , */ - data->event_window_id = strtol (p, &p, 10); - p++; /* Skip , */ - data->root_x = strtol (p, &p, 10); - p++; /* Skip , */ - data->root_y = strtol (p, &p, 10); - p++; /* Skip , */ - data->win_x = strtol (p, &p, 10); - p++; /* Skip , */ - data->win_y = strtol (p, &p, 10); - p++; /* Skip , */ - data->state = strtol (p, &p, 10); - - return p; -} - -static void -update_future_pointer_info (GdkBroadwayDisplay *broadway_display, BroadwayInputPointerMsg *data) -{ - broadway_display->future_root_x = data->root_x; - broadway_display->future_root_y = data->root_y; - broadway_display->future_state = data->state; - broadway_display->future_mouse_in_toplevel = data->mouse_window_id; -} - -static void -parse_input_message (BroadwayInput *input, const char *message) -{ - GdkBroadwayDisplay *broadway_display; - BroadwayInputMsg msg; - char *p; - gint64 time_; - - broadway_display = GDK_BROADWAY_DISPLAY (input->display); - - p = (char *)message; - msg.base.type = *p++; - msg.base.serial = (guint32)strtol (p, &p, 10); - p++; /* Skip , */ - time_ = strtol(p, &p, 10); - p++; /* Skip , */ - - if (time_ == 0) { - time_ = broadway_display->last_seen_time; - } else { - if (!input->seen_time) { - input->seen_time = TRUE; - /* Calculate time base so that any following times are normalized to start - 5 seconds after last_seen_time, to avoid issues that could appear when - a long hiatus due to a reconnect seems to be instant */ - input->time_base = time_ - (broadway_display->last_seen_time + 5000); - } - time_ = time_ - input->time_base; - } - - broadway_display->last_seen_time = time_; - - msg.base.time = time_; - - switch (msg.base.type) { - case 'e': /* Enter */ - case 'l': /* Leave */ - p = parse_pointer_data (p, &msg.pointer); - update_future_pointer_info (broadway_display, &msg.pointer); - p++; /* Skip , */ - msg.crossing.mode = strtol(p, &p, 10); - break; - - case 'm': /* Mouse move */ - p = parse_pointer_data (p, &msg.pointer); - update_future_pointer_info (broadway_display, &msg.pointer); - break; - - case 'b': - case 'B': - p = parse_pointer_data (p, &msg.pointer); - update_future_pointer_info (broadway_display, &msg.pointer); - p++; /* Skip , */ - msg.button.button = strtol(p, &p, 10); - break; - - case 's': - p = parse_pointer_data (p, &msg.pointer); - update_future_pointer_info (broadway_display, &msg.pointer); - p++; /* Skip , */ - msg.scroll.dir = strtol(p, &p, 10); - break; - - case 'k': - case 'K': - msg.key.key = strtol(p, &p, 10); - p++; /* Skip , */ - msg.key.state = strtol(p, &p, 10); - break; - - case 'g': - case 'u': - msg.grab_reply.res = strtol(p, &p, 10); - break; - - case 'w': - msg.configure_notify.id = strtol(p, &p, 10); - p++; /* Skip , */ - msg.configure_notify.x = strtol (p, &p, 10); - p++; /* Skip , */ - msg.configure_notify.y = strtol (p, &p, 10); - p++; /* Skip , */ - msg.configure_notify.width = strtol (p, &p, 10); - p++; /* Skip , */ - msg.configure_notify.height = strtol (p, &p, 10); - break; - - case 'W': - msg.delete_notify.id = strtol(p, &p, 10); - break; - - case 'd': - msg.screen_resize_notify.width = strtol (p, &p, 10); - p++; /* Skip , */ - msg.screen_resize_notify.height = strtol (p, &p, 10); - break; - - default: - g_printerr ("Unknown input command %s\n", message); - break; - } - - broadway_display->input_messages = g_list_append (broadway_display->input_messages, g_memdup (&msg, sizeof (msg))); - -} - -static inline void -hex_dump (guchar *data, gsize len) -{ -#ifdef DEBUG_WEBSOCKETS - gsize i, j; - for (j = 0; j < len + 15; j += 16) - { - fprintf (stderr, "0x%.4x ", j); - for (i = 0; i < 16; i++) - { - if ((j + i) < len) - fprintf (stderr, "%.2x ", data[j+i]); - else - fprintf (stderr, " "); - if (i == 8) - fprintf (stderr, " "); - } - fprintf (stderr, " | "); - - for (i = 0; i < 16; i++) - if ((j + i) < len && g_ascii_isalnum(data[j+i])) - fprintf (stderr, "%c", data[j+i]); - else - fprintf (stderr, "."); - fprintf (stderr, "\n"); - } -#endif -} - -static void -parse_input (BroadwayInput *input) -{ - GdkBroadwayDisplay *broadway_display; - - broadway_display = GDK_BROADWAY_DISPLAY (input->display); - - if (!input->buffer->len) - return; - - if (input->proto_v7_plus) - { - hex_dump (input->buffer->data, input->buffer->len); - - while (input->buffer->len > 2) - { - gsize len, payload_len; - BroadwayWSOpCode code; - gboolean is_mask, fin; - guchar *buf, *data, *mask; - - buf = input->buffer->data; - len = input->buffer->len; - -#ifdef DEBUG_WEBSOCKETS - g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]); -#endif - - fin = buf[0] & 0x80; - code = buf[0] & 0x0f; - payload_len = buf[1] & 0x7f; - is_mask = buf[1] & 0x80; - data = buf + 2; - - if (payload_len > 125) - { - if (len < 4) - return; - payload_len = GUINT16_FROM_BE( *(guint16 *) data ); - data += 2; - } - else if (payload_len > 126) - { - if (len < 10) - return; - payload_len = GUINT64_FROM_BE( *(guint64 *) data ); - data += 8; - } - - mask = NULL; - if (is_mask) - { - if (data - buf + 4 > len) - return; - mask = data; - data += 4; - } - - if (data - buf + payload_len > len) - return; /* wait to accumulate more */ - - if (is_mask) - { - gsize i; - for (i = 0; i < payload_len; i++) - data[i] ^= mask[i%4]; - } - - switch (code) { - case BROADWAY_WS_CNX_CLOSE: - break; /* hang around anyway */ - case BROADWAY_WS_TEXT: - if (!fin) - { -#ifdef DEBUG_WEBSOCKETS - g_warning ("can't yet accept fragmented input"); -#endif - } - else - { - char *terminated = g_strndup((char *)data, payload_len); - parse_input_message (input, terminated); - g_free (terminated); - } - break; - case BROADWAY_WS_CNX_PING: - broadway_output_pong (broadway_display->output); - break; - case BROADWAY_WS_CNX_PONG: - break; /* we never send pings, but tolerate pongs */ - case BROADWAY_WS_BINARY: - case BROADWAY_WS_CONTINUATION: - default: - { - g_warning ("fragmented or unknown input code 0x%2x with fin set", code); - break; - } - } - - g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len); - } - } - else /* old style protocol */ - { - char *buf, *ptr; - gsize len; - - buf = (char *)input->buffer->data; - len = input->buffer->len; - - if (buf[0] != 0) - { - broadway_display->input = NULL; - broadway_input_free (input); - return; - } - - while ((ptr = memchr (buf, 0xff, len)) != NULL) - { - *ptr = 0; - ptr++; - - parse_input_message (input, buf + 1); - - len -= ptr - buf; - buf = ptr; - - if (len > 0 && buf[0] != 0) - { - broadway_display->input = NULL; - broadway_input_free (input); - break; - } - } - g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data); - } -} - - -static gboolean -process_input_idle_cb (GdkBroadwayDisplay *display) -{ - display->process_input_idle = 0; - process_input_messages (display); - return G_SOURCE_REMOVE; -} - -static void -queue_process_input_at_idle (GdkBroadwayDisplay *broadway_display) -{ - if (broadway_display->process_input_idle == 0) - broadway_display->process_input_idle = - g_idle_add_full (GDK_PRIORITY_EVENTS, (GSourceFunc)process_input_idle_cb, broadway_display, NULL); -} - -static void -_gdk_broadway_display_read_all_input_nonblocking (GdkDisplay *display) -{ - GdkBroadwayDisplay *broadway_display; - GInputStream *in; - gssize res; - guint8 buffer[1024]; - GError *error; - BroadwayInput *input; - - broadway_display = GDK_BROADWAY_DISPLAY (display); - if (broadway_display->input == NULL) - return; - - input = broadway_display->input; - - in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); - - error = NULL; - res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in), - buffer, sizeof (buffer), NULL, &error); - - if (res <= 0) - { - if (res < 0 && - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) - { - g_error_free (error); - return; - } - - broadway_display->input = NULL; - broadway_input_free (input); - if (res < 0) - { - g_print ("input error %s\n", error->message); - g_error_free (error); - } - return; - } - - g_byte_array_append (input->buffer, buffer, res); - - parse_input (input); -} - -void -_gdk_broadway_display_consume_all_input (GdkDisplay *display) -{ - GdkBroadwayDisplay *broadway_display; - - broadway_display = GDK_BROADWAY_DISPLAY (display); - _gdk_broadway_display_read_all_input_nonblocking (display); - - /* Since we're parsing input but not processing the resulting messages - we might not get a readable callback on the stream, so queue an idle to - process the messages */ - queue_process_input_at_idle (broadway_display); -} - - -static gboolean -input_data_cb (GObject *stream, - BroadwayInput *input) -{ - GdkBroadwayDisplay *broadway_display; - - broadway_display = GDK_BROADWAY_DISPLAY (input->display); - _gdk_broadway_display_read_all_input_nonblocking (input->display); - - process_input_messages (broadway_display); - - return TRUE; -} - -/* Note: This may be called while handling a message (i.e. sorta recursively) */ -BroadwayInputMsg * -_gdk_broadway_display_block_for_input (GdkDisplay *display, char op, - guint32 serial, gboolean remove_message) -{ - GdkBroadwayDisplay *broadway_display; - BroadwayInputMsg *message; - gssize res; - guint8 buffer[1024]; - BroadwayInput *input; - GInputStream *in; - GList *l; - - gdk_display_flush (display); - - broadway_display = GDK_BROADWAY_DISPLAY (display); - if (broadway_display->input == NULL) - return NULL; - - input = broadway_display->input; - - while (TRUE) { - /* Check for existing reply in queue */ - - for (l = broadway_display->input_messages; l != NULL; l = l->next) - { - message = l->data; - - if (message->base.type == op) - { - if (message->base.serial == serial) - { - if (remove_message) - broadway_display->input_messages = - g_list_delete_link (broadway_display->input_messages, l); - return message; - } - } - } - - /* Not found, read more, blocking */ - - in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); - res = g_input_stream_read (in, buffer, sizeof (buffer), NULL, NULL); - if (res <= 0) - return NULL; - g_byte_array_append (input->buffer, buffer, res); - - parse_input (input); - - /* Since we're parsing input but not processing the resulting messages - we might not get a readable callback on the stream, so queue an idle to - process the messages */ - queue_process_input_at_idle (broadway_display); - } -} - -static char * -parse_line (char *line, char *key) -{ - char *p; - - if (!g_str_has_prefix (line, key)) - return NULL; - p = line + strlen (key); - if (*p != ':') - return NULL; - p++; - /* Skip optional initial space */ - if (*p == ' ') - p++; - return p; -} -static void -send_error (HttpRequest *request, - int error_code, - const char *reason) -{ - char *res; - - res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n" - "%d %s" - "%s", - error_code, reason, - error_code, reason, - reason); - /* TODO: This should really be async */ - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - res, strlen (res), NULL, NULL, NULL); - g_free (res); - http_request_free (request); -} - -/* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */ -#define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - -/* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */ -static gchar * -generate_handshake_response_wsietf_v7 (const gchar *key) -{ - gsize digest_len = 20; - guchar digest[digest_len]; - GChecksum *checksum; - - checksum = g_checksum_new (G_CHECKSUM_SHA1); - if (!checksum) - return NULL; - - g_checksum_update (checksum, (guchar *)key, -1); - g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1); - - g_checksum_get_digest (checksum, digest, &digest_len); - g_checksum_free (checksum); - - g_assert (digest_len == 20); - - return g_base64_encode (digest, digest_len); -} - -static void -start_input (HttpRequest *request, gboolean binary) -{ - char **lines; - char *p; - int num_key1, num_key2; - guint64 key1, key2; - int num_space; - int i; - guint8 challenge[16]; - char *res; - gsize len; - GChecksum *checksum; - char *origin, *host; - GdkBroadwayDisplay *broadway_display; - BroadwayInput *input; - const void *data_buffer; - gsize data_buffer_size; - GInputStream *in; - char *key_v7; - gboolean proto_v7_plus; - - broadway_display = GDK_BROADWAY_DISPLAY (request->display); - - if (broadway_display->input != NULL) - { - send_error (request, 409, "Input already handled"); - return; - } - -#ifdef DEBUG_WEBSOCKETS - g_print ("incoming request:\n%s\n", request->request->str); -#endif - lines = g_strsplit (request->request->str, "\n", 0); - - num_key1 = 0; - num_key2 = 0; - key1 = 0; - key2 = 0; - key_v7 = NULL; - origin = NULL; - host = NULL; - for (i = 0; lines[i] != NULL; i++) - { - if ((p = parse_line (lines[i], "Sec-WebSocket-Key1"))) - { - num_space = 0; - while (*p != 0) - { - if (g_ascii_isdigit (*p)) - key1 = key1 * 10 + g_ascii_digit_value (*p); - else if (*p == ' ') - num_space++; - - p++; - } - key1 /= num_space; - num_key1++; - } - else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2"))) - { - num_space = 0; - while (*p != 0) - { - if (g_ascii_isdigit (*p)) - key2 = key2 * 10 + g_ascii_digit_value (*p); - else if (*p == ' ') - num_space++; - - p++; - } - key2 /= num_space; - num_key2++; - } - else if ((p = parse_line (lines[i], "Sec-WebSocket-Key"))) - { - key_v7 = p; - } - else if ((p = parse_line (lines[i], "Origin"))) - { - origin = p; - } - else if ((p = parse_line (lines[i], "Host"))) - { - host = p; - } - else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin"))) - { - origin = p; - } - } - - if (origin == NULL || host == NULL) - { - g_strfreev (lines); - send_error (request, 400, "Bad websocket request"); - return; - } - - if (key_v7 != NULL) - { - char* accept = generate_handshake_response_wsietf_v7 (key_v7); - res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n" - "Sec-WebSocket-Origin: %s\r\n" - "Sec-WebSocket-Location: ws://%s/socket\r\n" - "Sec-WebSocket-Protocol: broadway\r\n" - "\r\n", accept, origin, host); - g_free (accept); - -#ifdef DEBUG_WEBSOCKETS - g_print ("v7 proto response:\n%s", res); -#endif - - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - res, strlen (res), NULL, NULL, NULL); - g_free (res); - proto_v7_plus = TRUE; - } - else - { - if (num_key1 != 1 || num_key2 != 1) - { - g_strfreev (lines); - send_error (request, 400, "Bad websocket request"); - return; - } - - challenge[0] = (key1 >> 24) & 0xff; - challenge[1] = (key1 >> 16) & 0xff; - challenge[2] = (key1 >> 8) & 0xff; - challenge[3] = (key1 >> 0) & 0xff; - challenge[4] = (key2 >> 24) & 0xff; - challenge[5] = (key2 >> 16) & 0xff; - challenge[6] = (key2 >> 8) & 0xff; - challenge[7] = (key2 >> 0) & 0xff; - - if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL)) - { - g_strfreev (lines); - send_error (request, 400, "Bad websocket request"); - return; - } - - checksum = g_checksum_new (G_CHECKSUM_MD5); - g_checksum_update (checksum, challenge, 16); - len = 16; - g_checksum_get_digest (checksum, challenge, &len); - g_checksum_free (checksum); - - res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: %s\r\n" - "Sec-WebSocket-Location: ws://%s/socket\r\n" - "Sec-WebSocket-Protocol: broadway\r\n" - "\r\n", - origin, host); - -#ifdef DEBUG_WEBSOCKETS - g_print ("legacy response:\n%s", res); -#endif - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - res, strlen (res), NULL, NULL, NULL); - g_free (res); - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - challenge, 16, NULL, NULL, NULL); - proto_v7_plus = FALSE; - } - - input = g_new0 (BroadwayInput, 1); - - input->display = request->display; - input->connection = g_object_ref (request->connection); - input->proto_v7_plus = proto_v7_plus; - input->binary = binary; - - data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size); - input->buffer = g_byte_array_sized_new (data_buffer_size); - g_byte_array_append (input->buffer, data_buffer, data_buffer_size); - - broadway_display->input = input; - - start_output (request, proto_v7_plus, binary); - - /* This will free and close the data input stream, but we got all the buffered content already */ - http_request_free (request); - - in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); - input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL); - g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL); - g_source_attach (input->source, NULL); - - /* Process any data in the pipe already */ - parse_input (input); - process_input_messages (broadway_display); - - g_strfreev (lines); -} - -static void -start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary) -{ - GSocket *socket; - GdkBroadwayDisplay *broadway_display; - int flag = 1; - - socket = g_socket_connection_get_socket (request->connection); - setsockopt(g_socket_get_fd (socket), IPPROTO_TCP, - TCP_NODELAY, (char *) &flag, sizeof(int)); - - broadway_display = GDK_BROADWAY_DISPLAY (request->display); - - if (broadway_display->output) - { - broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output); - broadway_output_free (broadway_display->output); - } - - broadway_display->output = - broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - broadway_display->saved_serial, proto_v7_plus, binary); - - _gdk_broadway_resync_windows (); - - if (broadway_display->pointer_grab_window) - broadway_output_grab_pointer (broadway_display->output, - GDK_WINDOW_IMPL_BROADWAY (broadway_display->pointer_grab_window->impl)->id, - broadway_display->pointer_grab_owner_events); -} - -static void -send_data (HttpRequest *request, - const char *mimetype, - const char *data, gsize len) -{ - char *res; - - res = g_strdup_printf ("HTTP/1.0 200 OK\r\n" - "Content-Type: %s\r\n" - "Content-Length: %"G_GSIZE_FORMAT"\r\n" - "\r\n", - mimetype, len); - /* TODO: This should really be async */ - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - res, strlen (res), NULL, NULL, NULL); - g_free (res); - g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - data, len, NULL, NULL, NULL); - http_request_free (request); -} - -#include "clienthtml.h" -#include "broadwayjs.h" - -static void -got_request (HttpRequest *request) -{ - char *start, *escaped, *tmp, *version, *query; - - if (!g_str_has_prefix (request->request->str, "GET ")) - { - send_error (request, 501, "Only GET implemented"); - return; - } - - start = request->request->str + 4; /* Skip "GET " */ - - while (*start == ' ') - start++; - - for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) - ; - escaped = g_strndup (start, tmp - start); - version = NULL; - if (*tmp == ' ') - { - start = tmp; - while (*start == ' ') - start++; - for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) - ; - version = g_strndup (start, tmp - start); - } - - query = strchr (escaped, '?'); - if (query) - *query = 0; - - if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0) - send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1); - else if (strcmp (escaped, "/broadway.js") == 0) - send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1); - else if (strcmp (escaped, "/socket") == 0) - start_input (request, FALSE); - else if (strcmp (escaped, "/socket-bin") == 0) - start_input (request, TRUE); - else - send_error (request, 404, "File not found"); - - g_free (escaped); - g_free (version); -} - -static void -got_http_request_line (GInputStream *stream, - GAsyncResult *result, - HttpRequest *request) -{ - char *line; - - line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL); - if (line == NULL) - { - http_request_free (request); - g_printerr ("Error reading request lines\n"); - return; - } - if (strlen (line) == 0) - got_request (request); - else - { - /* Protect against overflow in request length */ - if (request->request->len > 1024 * 5) - { - send_error (request, 400, "Request too long"); - } - else - { - g_string_append_printf (request->request, "%s\n", line); - g_data_input_stream_read_line_async (request->data, 0, NULL, - (GAsyncReadyCallback)got_http_request_line, request); - } - } - g_free (line); -} - -static gboolean -handle_incoming_connection (GSocketService *service, - GSocketConnection *connection, - GObject *source_object) -{ - HttpRequest *request; - GInputStream *in; - - request = g_new0 (HttpRequest, 1); - request->connection = g_object_ref (connection); - request->display = (GdkDisplay *) source_object; - request->request = g_string_new (""); - - in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); - - request->data = g_data_input_stream_new (in); - g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE); - /* Be tolerant of input */ - g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY); - - g_data_input_stream_read_line_async (request->data, 0, NULL, - (GAsyncReadyCallback)got_http_request_line, request); - return TRUE; -} - GdkDisplay * _gdk_broadway_display_open (const gchar *display_name) { GdkDisplay *display; GdkBroadwayDisplay *broadway_display; - GError *error = NULL; int port; display = g_object_new (GDK_TYPE_BROADWAY_DISPLAY, NULL); broadway_display = GDK_BROADWAY_DISPLAY (display); - broadway_display->output = NULL; - /* initialize the display's screens */ broadway_display->screens = g_new (GdkScreen *, 1); broadway_display->screens[0] = _gdk_broadway_screen_new (display, 0); @@ -1098,17 +161,12 @@ _gdk_broadway_display_open (const gchar *display_name) if (port == 0) port = 8080; - broadway_display->service = g_socket_service_new (); - if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (broadway_display->service), - port, - G_OBJECT (display), - &error)) + broadway_display->server = _gdk_broadway_server_new (port, NULL); + if (broadway_display->server == NULL) { - g_printerr ("Unable to listen to port %d: %s\n", port, error->message); - g_error_free (error); + g_printerr ("Unable to init server\n"); return NULL; } - g_signal_connect (broadway_display->service, "incoming", G_CALLBACK (handle_incoming_connection), NULL); g_signal_emit_by_name (display, "opened"); g_signal_emit_by_name (gdk_display_manager_get (), "display-opened", display); @@ -1116,7 +174,6 @@ _gdk_broadway_display_open (const gchar *display_name) return display; } - static const gchar * gdk_broadway_display_get_name (GdkDisplay *display) { @@ -1160,8 +217,11 @@ gdk_broadway_display_beep (GdkDisplay *display) static void gdk_broadway_display_sync (GdkDisplay *display) { - g_return_if_fail (GDK_IS_DISPLAY (display)); + GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display); + g_return_if_fail (GDK_IS_BROADWAY_DISPLAY (display)); + + _gdk_broadway_server_sync (broadway_display->server); } static void @@ -1169,15 +229,9 @@ gdk_broadway_display_flush (GdkDisplay *display) { GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display); - g_return_if_fail (GDK_IS_DISPLAY (display)); + g_return_if_fail (GDK_IS_BROADWAY_DISPLAY (display)); - if (broadway_display->output && - !broadway_output_flush (broadway_display->output)) - { - broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output); - broadway_output_free (broadway_display->output); - broadway_display->output = NULL; - } + _gdk_broadway_server_flush (broadway_display->server); } static gboolean @@ -1307,11 +361,9 @@ gdk_broadway_display_get_next_serial (GdkDisplay *display) { GdkBroadwayDisplay *broadway_display; broadway_display = GDK_BROADWAY_DISPLAY (display); - if (broadway_display->output) - return broadway_output_get_next_serial (broadway_display->output); - return broadway_display->saved_serial; -} + return _gdk_broadway_server_get_next_serial (broadway_display->server); +} static void gdk_broadway_display_event_data_copy (GdkDisplay *display, diff --git a/gdk/broadway/gdkdisplay-broadway.h b/gdk/broadway/gdkdisplay-broadway.h index d3699f8438..db24a011cd 100644 --- a/gdk/broadway/gdkdisplay-broadway.h +++ b/gdk/broadway/gdkdisplay-broadway.h @@ -27,6 +27,7 @@ #include "gdkwindow.h" #include "gdkinternals.h" #include "gdkmain.h" +#include "gdkbroadway-server.h" #include "broadway.h" G_BEGIN_DECLS @@ -43,82 +44,6 @@ typedef struct BroadwayInput BroadwayInput; #define GDK_IS_BROADWAY_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_DISPLAY)) #define GDK_BROADWAY_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_DISPLAY, GdkBroadwayDisplayClass)) -typedef struct { - char type; - guint32 serial; - guint64 time; -} BroadwayInputBaseMsg; - -typedef struct { - BroadwayInputBaseMsg base; - guint32 mouse_window_id; /* The real window, not taking grabs into account */ - guint32 event_window_id; - int root_x; - int root_y; - int win_x; - int win_y; - guint32 state; -} BroadwayInputPointerMsg; - -typedef struct { - BroadwayInputPointerMsg pointer; - guint32 mode; -} BroadwayInputCrossingMsg; - -typedef struct { - BroadwayInputPointerMsg pointer; - guint32 button; -} BroadwayInputButtonMsg; - -typedef struct { - BroadwayInputPointerMsg pointer; - int dir; -} BroadwayInputScrollMsg; - -typedef struct { - BroadwayInputBaseMsg base; - guint32 state; - int key; -} BroadwayInputKeyMsg; - -typedef struct { - BroadwayInputBaseMsg base; - int res; -} BroadwayInputGrabReply; - -typedef struct { - BroadwayInputBaseMsg base; - int id; - int x; - int y; - int width; - int height; -} BroadwayInputConfigureNotify; - -typedef struct { - BroadwayInputBaseMsg base; - int width; - int height; -} BroadwayInputScreenResizeNotify; - -typedef struct { - BroadwayInputBaseMsg base; - int id; -} BroadwayInputDeleteNotify; - -typedef union { - BroadwayInputBaseMsg base; - BroadwayInputPointerMsg pointer; - BroadwayInputCrossingMsg crossing; - BroadwayInputButtonMsg button; - BroadwayInputScrollMsg scroll; - BroadwayInputKeyMsg key; - BroadwayInputGrabReply grab_reply; - BroadwayInputConfigureNotify configure_notify; - BroadwayInputDeleteNotify delete_notify; - BroadwayInputScreenResizeNotify screen_resize_notify; -} BroadwayInputMsg; - struct _GdkBroadwayDisplay { GdkDisplay parent_instance; @@ -129,10 +54,6 @@ struct _GdkBroadwayDisplay GList *toplevels; GSource *event_source; - GdkWindow *mouse_in_toplevel; - int last_x, last_y; /* in root coords */ - guint32 last_state; - GdkWindow *real_mouse_in_toplevel; /* Not affected by grabs */ /* Keyboard related information */ GdkKeymap *keymap; @@ -147,24 +68,7 @@ struct _GdkBroadwayDisplay /* The offscreen window that has the pointer in it (if any) */ GdkWindow *active_offscreen_window; - GSocketService *service; - BroadwayOutput *output; - guint32 saved_serial; - guint64 last_seen_time; - BroadwayInput *input; - GList *input_messages; - guint process_input_idle; - - /* Explicit pointer grabs: */ - GdkWindow *pointer_grab_window; - guint32 pointer_grab_time; - gboolean pointer_grab_owner_events; - - /* Future data, from the currently queued events */ - int future_root_x; - int future_root_y; - GdkModifierType future_state; - int future_mouse_in_toplevel; + GdkBroadwayServer *server; }; struct _GdkBroadwayDisplayClass diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c index 14fa4793c0..ff8430dbc0 100644 --- a/gdk/broadway/gdkeventsource.c +++ b/gdk/broadway/gdkeventsource.c @@ -87,9 +87,9 @@ gdk_event_source_check (GSource *source) } void -_gdk_broadway_events_got_input (GdkDisplay *display, - BroadwayInputMsg *message) +_gdk_broadway_events_got_input (BroadwayInputMsg *message) { + GdkDisplay *display = gdk_display_get_default (); GdkBroadwayDisplay *display_broadway = GDK_BROADWAY_DISPLAY (display); GdkScreen *screen; GdkWindow *window; @@ -98,16 +98,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, switch (message->base.type) { case 'e': /* Enter */ - display_broadway->last_x = message->pointer.root_x; - display_broadway->last_y = message->pointer.root_y; - display_broadway->last_state = message->pointer.state; - display_broadway->real_mouse_in_toplevel = - g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id)); - window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id)); - - /* TODO: Unset when it dies */ - display_broadway->mouse_in_toplevel = window; if (window) { event = gdk_event_new (GDK_ENTER_NOTIFY); @@ -135,15 +126,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, } break; case 'l': /* Leave */ - display_broadway->last_x = message->pointer.root_x; - display_broadway->last_y = message->pointer.root_y; - display_broadway->last_state = message->pointer.state; - display_broadway->real_mouse_in_toplevel = - g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id)); - window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id)); - - display_broadway->mouse_in_toplevel = NULL; if (window) { event = gdk_event_new (GDK_LEAVE_NOTIFY); @@ -171,12 +154,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display, } break; case 'm': /* Mouse move */ - display_broadway->last_x = message->pointer.root_x; - display_broadway->last_y = message->pointer.root_y; - display_broadway->last_state = message->pointer.state; - display_broadway->real_mouse_in_toplevel = - g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id)); - if (_gdk_broadway_moveresize_handle_event (display, message)) break; @@ -200,12 +177,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display, break; case 'b': case 'B': - display_broadway->last_x = message->pointer.root_x; - display_broadway->last_y = message->pointer.root_y; - display_broadway->last_state = message->pointer.state; - display_broadway->real_mouse_in_toplevel = - g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id)); - if (message->base.type != 'b' && _gdk_broadway_moveresize_handle_event (display, message)) break; @@ -230,12 +201,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display, break; case 's': - display_broadway->last_x = message->pointer.root_x; - display_broadway->last_y = message->pointer.root_y; - display_broadway->last_state = message->pointer.state; - display_broadway->real_mouse_in_toplevel = - g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id)); - window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id)); if (window) { @@ -256,8 +221,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display, break; case 'k': case 'K': - window = display_broadway->mouse_in_toplevel; - + window = g_hash_table_lookup (display_broadway->id_ht, + GINT_TO_POINTER (message->key.mouse_window_id)); if (window) { event = gdk_event_new (message->base.type == 'k' ? GDK_KEY_PRESS : GDK_KEY_RELEASE); @@ -269,8 +234,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display, event->key.length = 0; gdk_event_set_device (event, display->core_pointer); - display_broadway->last_state = message->key.state; - node = _gdk_event_queue_append (display, event); _gdk_windowing_got_event (display, node, event, message->base.serial); } @@ -335,7 +298,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, break; default: - g_printerr ("Unknown input command %c\n", message->base.type); + g_printerr ("_gdk_broadway_events_got_input - Unknown input command %c\n", message->base.type); break; } } diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h index bcc06d23d4..8277c0880a 100644 --- a/gdk/broadway/gdkprivate-broadway.h +++ b/gdk/broadway/gdkprivate-broadway.h @@ -126,8 +126,7 @@ GList *_gdk_broadway_screen_list_visuals (GdkScreen *screen); void _gdk_broadway_screen_size_changed (GdkScreen *screen, BroadwayInputScreenResizeNotify *msg); -void _gdk_broadway_events_got_input (GdkDisplay *display, - BroadwayInputMsg *message); +void _gdk_broadway_events_got_input (BroadwayInputMsg *message); void _gdk_broadway_screen_init_root_window (GdkScreen *screen); void _gdk_broadway_screen_init_visuals (GdkScreen *screen); diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c index 08e23afea5..9709ff6864 100644 --- a/gdk/broadway/gdkwindow-broadway.c +++ b/gdk/broadway/gdkwindow-broadway.c @@ -83,95 +83,17 @@ G_DEFINE_TYPE (GdkWindowImplBroadway, gdk_window_impl_broadway, GDK_TYPE_WINDOW_IMPL) -static void -diff_surfaces (cairo_surface_t *surface, - cairo_surface_t *old_surface) -{ - guint8 *data, *old_data; - guint32 *line, *old_line; - int w, h, stride, old_stride; - int x, y; - - data = cairo_image_surface_get_data (surface); - old_data = cairo_image_surface_get_data (old_surface); - - w = cairo_image_surface_get_width (surface); - h = cairo_image_surface_get_height (surface); - - stride = cairo_image_surface_get_stride (surface); - old_stride = cairo_image_surface_get_stride (old_surface); - - for (y = 0; y < h; y++) - { - line = (guint32 *)data; - old_line = (guint32 *)old_data; - - for (x = 0; x < w; x++) - { - if ((*line & 0xffffff) == (*old_line & 0xffffff)) - *old_line = 0; - else - *old_line = *line | 0xff000000; - line ++; - old_line ++; - } - - data += stride; - old_data += old_stride; - } -} - static guint dirty_flush_id = 0; -static void -window_data_send (BroadwayOutput *output, GdkWindowImplBroadway *impl) -{ - cairo_t *cr; - - if (impl->surface == NULL) - return; - - if (impl->last_synced) - { - diff_surfaces (impl->surface, - impl->last_surface); - broadway_output_put_rgba (output, impl->id, 0, 0, - cairo_image_surface_get_width (impl->last_surface), - cairo_image_surface_get_height (impl->last_surface), - cairo_image_surface_get_stride (impl->last_surface), - cairo_image_surface_get_data (impl->last_surface)); - } - else - { - impl->last_synced = TRUE; - broadway_output_put_rgb (output, impl->id, 0, 0, - cairo_image_surface_get_width (impl->surface), - cairo_image_surface_get_height (impl->surface), - cairo_image_surface_get_stride (impl->surface), - cairo_image_surface_get_data (impl->surface)); - } - - broadway_output_surface_flush (output, impl->id); - - cr = cairo_create (impl->last_surface); - cairo_set_source_surface (cr, impl->surface, 0, 0); - cairo_paint (cr); - cairo_destroy (cr); -} - static gboolean dirty_flush_idle (gpointer data) { GList *l; GdkBroadwayDisplay *display; - BroadwayOutput *output; dirty_flush_id = 0; display = GDK_BROADWAY_DISPLAY (gdk_display_get_default ()); - output = display->output; - if (output == NULL) - return FALSE; for (l = display->toplevels; l != NULL; l = l->next) { @@ -180,11 +102,15 @@ dirty_flush_idle (gpointer data) if (impl->dirty) { impl->dirty = FALSE; - window_data_send (display->output, impl); + _gdk_broadway_server_window_update (display->server, + impl->id, + impl->surface); } } - gdk_display_flush (GDK_DISPLAY (display)); + /* We sync here to ensure all references to the impl->surface memory + is done, as we may later paint new data in them. */ + gdk_display_sync (GDK_DISPLAY (display)); return FALSE; } @@ -192,64 +118,10 @@ dirty_flush_idle (gpointer data) static void queue_dirty_flush (GdkBroadwayDisplay *display) { - if (dirty_flush_id == 0 && display->output != NULL) + if (dirty_flush_id == 0) dirty_flush_id = gdk_threads_add_idle (dirty_flush_idle, NULL); } -void -_gdk_broadway_resync_windows (void) -{ - GdkBroadwayDisplay *display; - GList *l; - - dirty_flush_id = 0; - - display = GDK_BROADWAY_DISPLAY (gdk_display_get_default ()); - - /* First create all windows */ - for (l = display->toplevels; l != NULL; l = l->next) - { - GdkWindowImplBroadway *impl = l->data; - GdkWindow *window; - - window = impl->wrapper; - - if (impl->id == 0) - continue; /* Skip root */ - - impl->dirty = FALSE; - impl->last_synced = FALSE; - broadway_output_new_surface (display->output, - impl->id, - window->x, - window->y, - window->width, - window->height, - window->window_type == GDK_WINDOW_TEMP); - } - - /* Then do everything that may reference other windows */ - for (l = display->toplevels; l != NULL; l = l->next) - { - GdkWindowImplBroadway *impl = l->data; - - if (impl->id == 0) - continue; /* Skip root */ - - if (impl->transient_for) - broadway_output_set_transient_for (display->output, impl->id, impl->transient_for); - /* Can't check GDK_WINDOW_IS_MAPPED here, because that doesn't correctly handle - withdrawn windows like menus */ - if (impl->visible) - { - broadway_output_show_surface (display->output, impl->id); - window_data_send (display->output, impl); - } - } - - gdk_display_flush (GDK_DISPLAY (display)); -} - static void gdk_window_impl_broadway_init (GdkWindowImplBroadway *impl) { @@ -275,12 +147,6 @@ gdk_window_impl_broadway_finalize (GObject *object) broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (impl->wrapper)); - if (broadway_display->mouse_in_toplevel == GDK_WINDOW (wrapper)) - { - /* TODO: Send leave + enter event, update cursors, etc */ - broadway_display->mouse_in_toplevel = NULL; - } - g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id)); if (impl->cursor) @@ -315,6 +181,7 @@ _gdk_broadway_screen_init_root_window (GdkScreen * screen) impl->screen = screen; impl->wrapper = window; + impl->id = 0; window->window_type = GDK_WINDOW_ROOT; window->depth = 24; @@ -341,13 +208,17 @@ _gdk_broadway_display_create_window_impl (GdkDisplay *display, { GdkWindowImplBroadway *impl; GdkBroadwayDisplay *broadway_display; - static int current_id = 1; /* 0 is the root window */ broadway_display = GDK_BROADWAY_DISPLAY (display); impl = g_object_new (GDK_TYPE_WINDOW_IMPL_BROADWAY, NULL); window->impl = (GdkWindowImpl *)impl; - impl->id = current_id++; + impl->id = _gdk_broadway_server_new_window (broadway_display->server, + window->x, + window->y, + window->width, + window->height, + window->window_type == GDK_WINDOW_TEMP); g_hash_table_insert (broadway_display->id_ht, GINT_TO_POINTER(impl->id), window); impl->wrapper = window; @@ -358,37 +229,23 @@ _gdk_broadway_display_create_window_impl (GdkDisplay *display, g_assert (GDK_WINDOW_TYPE (window->parent) == GDK_WINDOW_ROOT); broadway_display->toplevels = g_list_prepend (broadway_display->toplevels, impl); - - if (broadway_display->output) - broadway_output_new_surface (broadway_display->output, - impl->id, - window->x, - window->y, - window->width, - window->height, - window->window_type == GDK_WINDOW_TEMP); } void _gdk_broadway_window_resize_surface (GdkWindow *window) { GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl); - cairo_surface_t *old, *last_old; + cairo_surface_t *old; if (impl->surface) { old = impl->surface; - last_old = impl->last_surface; impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, gdk_window_get_width (impl->wrapper), gdk_window_get_height (impl->wrapper)); - impl->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - gdk_window_get_width (impl->wrapper), - gdk_window_get_height (impl->wrapper)); cairo_surface_destroy (old); - cairo_surface_destroy (last_old); } if (impl->ref_surface) @@ -413,7 +270,6 @@ static cairo_surface_t * gdk_window_broadway_ref_cairo_surface (GdkWindow *window) { GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl); - cairo_t *cr; int w, h; if (GDK_IS_WINDOW_IMPL_BROADWAY (window) && @@ -425,22 +281,7 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window) /* Create actual backing store if missing */ if (!impl->surface) - { - impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); - impl->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); - - cr = cairo_create (impl->surface); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); - cairo_rectangle (cr, 0, 0, w, h); - cairo_fill (cr); - cairo_destroy (cr); - - cr = cairo_create (impl->last_surface); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); - cairo_rectangle (cr, 0, 0, w, h); - cairo_fill (cr); - cairo_destroy (cr); - } + impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); /* Create a destroyable surface referencing the real one */ if (!impl->ref_surface) @@ -485,16 +326,13 @@ _gdk_broadway_window_destroy (GdkWindow *window, { cairo_surface_destroy (impl->surface); impl->surface = NULL; - cairo_surface_destroy (impl->last_surface); - impl->last_surface = NULL; } broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window)); g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id)); - if (broadway_display->output) - broadway_output_destroy_surface (broadway_display->output, - impl->id); + _gdk_broadway_server_destroy_window (broadway_display->server, + impl->id); } static cairo_surface_t * @@ -546,11 +384,9 @@ gdk_window_broadway_show (GdkWindow *window, gboolean already_mapped) _gdk_make_event (GDK_WINDOW (window), GDK_MAP, NULL, FALSE); broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window)); - if (broadway_display->output) - { - broadway_output_show_surface (broadway_display->output, impl->id); - queue_dirty_flush (broadway_display); - } + if (_gdk_broadway_server_window_show (broadway_display->server, impl->id)) + queue_dirty_flush (broadway_display); + } static void @@ -569,17 +405,8 @@ gdk_window_broadway_hide (GdkWindow *window) _gdk_make_event (GDK_WINDOW (window), GDK_UNMAP, NULL, FALSE); broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window)); - if (broadway_display->output) - { - broadway_output_hide_surface (broadway_display->output, impl->id); - queue_dirty_flush (broadway_display); - } - - if (broadway_display->mouse_in_toplevel == window) - { - /* TODO: Send leave + enter event, update cursors, etc */ - broadway_display->mouse_in_toplevel = NULL; - } + if (_gdk_broadway_server_window_hide (broadway_display->server, impl->id)) + queue_dirty_flush (broadway_display); _gdk_window_clear_update_area (window); } @@ -601,7 +428,6 @@ gdk_window_broadway_move_resize (GdkWindow *window, GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl); GdkBroadwayDisplay *broadway_display; gboolean changed, size_changed;; - gboolean with_resize; size_changed = changed = FALSE; @@ -613,10 +439,8 @@ gdk_window_broadway_move_resize (GdkWindow *window, window->y = y; } - with_resize = FALSE; if (width > 0 || height > 0) { - with_resize = TRUE; if (width < 1) width = 1; @@ -644,12 +468,11 @@ gdk_window_broadway_move_resize (GdkWindow *window, GdkEvent *event; GList *node; - if (broadway_display->output != NULL) + if (_gdk_broadway_server_window_move_resize (broadway_display->server, + impl->id, + window->x, window->y, + window->width, window->height)) { - broadway_output_move_resize_surface (broadway_display->output, - impl->id, - with_move, window->x, window->y, - with_resize, window->width, window->height); queue_dirty_flush (broadway_display); if (size_changed) window->resize_count++; @@ -787,11 +610,7 @@ gdk_broadway_window_set_transient_for (GdkWindow *window, impl->transient_for = parent_id; display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (impl->wrapper)); - if (display->output) - { - broadway_output_set_transient_for (display->output, impl->id, impl->transient_for); - gdk_display_flush (GDK_DISPLAY (display)); - } + _gdk_broadway_server_window_set_transient_for (display->server, impl->id, impl->transient_for); } static void @@ -1324,20 +1143,10 @@ moveresize_lookahead (GdkDisplay *display, BroadwayInputMsg *event) { GdkBroadwayDisplay *broadway_display; - BroadwayInputMsg *message; - GList *l; broadway_display = GDK_BROADWAY_DISPLAY (display); - for (l = broadway_display->input_messages; l != NULL; l = l->next) - { - message = l->data; - if (message->base.type == 'm') - return FALSE; - if (message->base.type == 'b') - return FALSE; - } - return TRUE; + return !_gdk_broadway_server_lookahead_event (broadway_display->server, "mb"); } gboolean @@ -1480,7 +1289,7 @@ gdk_broadway_window_begin_resize_drag (GdkWindow *window, /* We need a connection to be able to get mouse events, if not, punt */ broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window)); - if (!broadway_display->output) + if (!_gdk_broadway_server_has_client (broadway_display->server)) return; mv_resize = get_move_resize_data (GDK_WINDOW_DISPLAY (window), TRUE); @@ -1622,9 +1431,6 @@ _gdk_broadway_window_translate (GdkWindow *window, { GdkWindowImplBroadway *impl; GdkBroadwayDisplay *broadway_display; - int n_rects, i; - BroadwayRect *rects; - cairo_rectangle_int_t rect; impl = GDK_WINDOW_IMPL_BROADWAY (window->impl); @@ -1632,26 +1438,11 @@ _gdk_broadway_window_translate (GdkWindow *window, { copy_region (impl->surface, area, dx, dy); broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window)); - if (GDK_WINDOW_IMPL_BROADWAY (impl)->last_synced && - broadway_display->output) - { - copy_region (impl->last_surface, area, dx, dy); - n_rects = cairo_region_num_rectangles (area); - rects = g_new (BroadwayRect, n_rects); - for (i = 0; i < n_rects; i++) - { - cairo_region_get_rectangle (area, i, &rect); - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - broadway_output_copy_rectangles (broadway_display->output, - GDK_WINDOW_IMPL_BROADWAY (impl)->id, - rects, n_rects, dx, dy); - queue_dirty_flush (broadway_display); - g_free (rects); - } + + if (_gdk_broadway_server_window_translate (broadway_display->server, + impl->id, + area, dx, dy)) + queue_dirty_flush (broadway_display); } } @@ -1661,8 +1452,7 @@ gdk_broadway_get_last_seen_time (GdkWindow *window) GdkDisplay *display; display = gdk_window_get_display (window); - _gdk_broadway_display_consume_all_input (display); - return (guint32) GDK_BROADWAY_DISPLAY (display)->last_seen_time; + return _gdk_broadway_server_get_last_seen_time (GDK_BROADWAY_DISPLAY (display)->server); } static void