File: | cdk/broadway/broadway-server.c |
Warning: | line 1247, column 11 Potential leak of memory pointed to by 'request' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include "config.h" | |||
2 | ||||
3 | #include "broadway-server.h" | |||
4 | ||||
5 | #include "broadway-output.h" | |||
6 | ||||
7 | #include <glib.h> | |||
8 | #include <glib/gprintf.h> | |||
9 | #include "cdktypes.h" | |||
10 | #include <stdlib.h> | |||
11 | #include <string.h> | |||
12 | #include <errno(*__errno_location ()).h> | |||
13 | ||||
14 | #ifdef HAVE_UNISTD_H1 | |||
15 | #include <unistd.h> | |||
16 | #elif defined (G_OS_WIN32) | |||
17 | #include <io.h> | |||
18 | #endif | |||
19 | #ifdef HAVE_SYS_MMAN_H1 | |||
20 | #include <sys/mman.h> | |||
21 | #endif | |||
22 | #include <sys/stat.h> | |||
23 | #include <fcntl.h> | |||
24 | #include <sys/types.h> | |||
25 | #ifdef G_OS_UNIX | |||
26 | #include <sys/socket.h> | |||
27 | #include <netinet/in.h> | |||
28 | #include <netinet/tcp.h> | |||
29 | #endif | |||
30 | #ifdef HAVE_GIO_UNIX1 | |||
31 | #include <gio/gunixsocketaddress.h> | |||
32 | #endif | |||
33 | #ifdef G_OS_WIN32 | |||
34 | #include <windows.h> | |||
35 | #include <string.h> | |||
36 | #endif | |||
37 | ||||
38 | typedef struct BroadwayInput BroadwayInput; | |||
39 | typedef struct BroadwayWindow BroadwayWindow; | |||
40 | struct _BroadwayServer { | |||
41 | GObject parent_instance; | |||
42 | ||||
43 | char *address; | |||
44 | int port; | |||
45 | char *ssl_cert; | |||
46 | char *ssl_key; | |||
47 | GSocketService *service; | |||
48 | BroadwayOutput *output; | |||
49 | guint32 id_counter; | |||
50 | guint32 saved_serial; | |||
51 | guint64 last_seen_time; | |||
52 | BroadwayInput *input; | |||
53 | GList *input_messages; | |||
54 | guint process_input_idle; | |||
55 | ||||
56 | GHashTable *id_ht; | |||
57 | GList *toplevels; | |||
58 | BroadwayWindow *root; | |||
59 | gint32 focused_window_id; /* -1 => none */ | |||
60 | gint show_keyboard; | |||
61 | ||||
62 | guint32 screen_width; | |||
63 | guint32 screen_height; | |||
64 | ||||
65 | gint32 mouse_in_toplevel_id; | |||
66 | int last_x, last_y; /* in root coords */ | |||
67 | guint32 last_state; | |||
68 | gint32 real_mouse_in_toplevel_id; /* Not affected by grabs */ | |||
69 | ||||
70 | /* Explicit pointer grabs: */ | |||
71 | gint32 pointer_grab_window_id; /* -1 => none */ | |||
72 | gint32 pointer_grab_client_id; /* -1 => none */ | |||
73 | guint32 pointer_grab_time; | |||
74 | gboolean pointer_grab_owner_events; | |||
75 | ||||
76 | /* Future data, from the currently queued events */ | |||
77 | int future_root_x; | |||
78 | int future_root_y; | |||
79 | guint32 future_state; | |||
80 | int future_mouse_in_toplevel; | |||
81 | }; | |||
82 | ||||
83 | struct _BroadwayServerClass | |||
84 | { | |||
85 | GObjectClass parent_class; | |||
86 | }; | |||
87 | ||||
88 | typedef struct HttpRequest { | |||
89 | BroadwayServer *server; | |||
90 | GSocketConnection *socket_connection; | |||
91 | GIOStream *connection; | |||
92 | GDataInputStream *data; | |||
93 | GString *request; | |||
94 | } HttpRequest; | |||
95 | ||||
96 | struct BroadwayInput { | |||
97 | BroadwayServer *server; | |||
98 | BroadwayOutput *output; | |||
99 | GIOStream *connection; | |||
100 | GByteArray *buffer; | |||
101 | GSource *source; | |||
102 | gboolean seen_time; | |||
103 | gint64 time_base; | |||
104 | gboolean active; | |||
105 | }; | |||
106 | ||||
107 | struct BroadwayWindow { | |||
108 | gint32 id; | |||
109 | gint32 x; | |||
110 | gint32 y; | |||
111 | gint32 width; | |||
112 | gint32 height; | |||
113 | gboolean is_temp; | |||
114 | gboolean visible; | |||
115 | gint32 transient_for; | |||
116 | ||||
117 | BroadwayBuffer *buffer; | |||
118 | gboolean buffer_synced; | |||
119 | ||||
120 | char *cached_surface_name; | |||
121 | cairo_surface_t *cached_surface; | |||
122 | }; | |||
123 | ||||
124 | static void broadway_server_resync_windows (BroadwayServer *server); | |||
125 | ||||
126 | static GType broadway_server_get_type (void); | |||
127 | ||||
128 | G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)static void broadway_server_init (BroadwayServer *self); static void broadway_server_class_init (BroadwayServerClass *klass) ; static GType broadway_server_get_type_once (void); static gpointer broadway_server_parent_class = ((void*)0); static gint BroadwayServer_private_offset ; static void broadway_server_class_intern_init (gpointer klass ) { broadway_server_parent_class = g_type_class_peek_parent ( klass); if (BroadwayServer_private_offset != 0) g_type_class_adjust_private_offset (klass, &BroadwayServer_private_offset); broadway_server_class_init ((BroadwayServerClass*) klass); } __attribute__ ((__unused__ )) static inline gpointer broadway_server_get_instance_private (BroadwayServer *self) { return (((gpointer) ((guint8*) (self ) + (glong) (BroadwayServer_private_offset)))); } GType broadway_server_get_type (void) { static GType static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); (void) ( 0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0)) ; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id ) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id ); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5) ; gapg_temp_newval; })) && g_once_init_enter_pointer ( &static_g_define_type_id)); })) ) { GType g_define_type_id = broadway_server_get_type_once (); (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer) , "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id ) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer ((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id )); })) ; } return static_g_define_type_id; } __attribute__ ( (__noinline__)) static GType broadway_server_get_type_once (void ) { GType g_define_type_id = g_type_register_static_simple (( (GType) ((20) << (2))), g_intern_static_string ("BroadwayServer" ), sizeof (BroadwayServerClass), (GClassInitFunc)(void (*)(void )) broadway_server_class_intern_init, sizeof (BroadwayServer) , (GInstanceInitFunc)(void (*)(void)) broadway_server_init, ( GTypeFlags) 0); { {{};} } return g_define_type_id; } | |||
129 | ||||
130 | static void | |||
131 | broadway_server_init (BroadwayServer *server) | |||
132 | { | |||
133 | BroadwayWindow *root; | |||
134 | ||||
135 | server->service = g_socket_service_new (); | |||
136 | server->pointer_grab_window_id = -1; | |||
137 | server->saved_serial = 1; | |||
138 | server->last_seen_time = 1; | |||
139 | server->id_ht = g_hash_table_new (NULL((void*)0), NULL((void*)0)); | |||
140 | server->id_counter = 0; | |||
141 | ||||
142 | root = g_new0 (BroadwayWindow, 1)((BroadwayWindow *) g_malloc0_n ((1), sizeof (BroadwayWindow) )); | |||
143 | root->id = server->id_counter++; | |||
144 | root->width = 1024; | |||
145 | root->height = 768; | |||
146 | root->visible = TRUE(!(0)); | |||
147 | ||||
148 | server->root = root; | |||
149 | ||||
150 | g_hash_table_insert (server->id_ht, | |||
151 | GINT_TO_POINTER (root->id)((gpointer) (glong) (root->id)), | |||
152 | root); | |||
153 | } | |||
154 | ||||
155 | static void | |||
156 | broadway_server_finalize (GObject *object) | |||
157 | { | |||
158 | BroadwayServer *server = BROADWAY_SERVER (object)((((BroadwayServer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((object)), ((broadway_server_get_type())))))); | |||
159 | ||||
160 | g_free (server->address); | |||
161 | g_free (server->ssl_cert); | |||
162 | g_free (server->ssl_key); | |||
163 | ||||
164 | G_OBJECT_CLASS (broadway_server_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((broadway_server_parent_class)), (((GType) ((20) << (2))))))))->finalize (object); | |||
165 | } | |||
166 | ||||
167 | static void | |||
168 | broadway_server_class_init (BroadwayServerClass * class) | |||
169 | { | |||
170 | GObjectClass *object_class = G_OBJECT_CLASS (class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((class)), (((GType) ((20) << (2)))))))); | |||
171 | ||||
172 | object_class->finalize = broadway_server_finalize; | |||
173 | } | |||
174 | ||||
175 | static void start (BroadwayInput *input); | |||
176 | ||||
177 | static void | |||
178 | http_request_free (HttpRequest *request) | |||
179 | { | |||
180 | g_object_unref (request->socket_connection); | |||
181 | g_object_unref (request->connection); | |||
182 | g_object_unref (request->data); | |||
183 | g_string_free (request->request, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (request->request), ((!(0)))) : g_string_free_and_steal (request ->request)) : (g_string_free) ((request->request), ((!( 0))))); | |||
184 | g_free (request); | |||
185 | } | |||
186 | ||||
187 | static void | |||
188 | broadway_input_free (BroadwayInput *input) | |||
189 | { | |||
190 | g_object_unref (input->connection); | |||
191 | g_byte_array_free (input->buffer, FALSE(0)); | |||
192 | g_source_destroy (input->source); | |||
193 | g_free (input); | |||
194 | } | |||
195 | ||||
196 | static void | |||
197 | update_event_state (BroadwayServer *server, | |||
198 | BroadwayInputMsg *message) | |||
199 | { | |||
200 | BroadwayWindow *window; | |||
201 | ||||
202 | switch (message->base.type) { | |||
203 | case BROADWAY_EVENT_ENTER: | |||
204 | server->last_x = message->pointer.root_x; | |||
205 | server->last_y = message->pointer.root_y; | |||
206 | server->last_state = message->pointer.state; | |||
207 | server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; | |||
208 | ||||
209 | /* TODO: Unset when it dies */ | |||
210 | server->mouse_in_toplevel_id = message->pointer.event_window_id; | |||
211 | break; | |||
212 | case BROADWAY_EVENT_LEAVE: | |||
213 | server->last_x = message->pointer.root_x; | |||
214 | server->last_y = message->pointer.root_y; | |||
215 | server->last_state = message->pointer.state; | |||
216 | server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; | |||
217 | ||||
218 | server->mouse_in_toplevel_id = 0; | |||
219 | break; | |||
220 | case BROADWAY_EVENT_POINTER_MOVE: | |||
221 | server->last_x = message->pointer.root_x; | |||
222 | server->last_y = message->pointer.root_y; | |||
223 | server->last_state = message->pointer.state; | |||
224 | server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; | |||
225 | break; | |||
226 | case BROADWAY_EVENT_BUTTON_PRESS: | |||
227 | case BROADWAY_EVENT_BUTTON_RELEASE: | |||
228 | if (message->base.type == BROADWAY_EVENT_BUTTON_PRESS && | |||
229 | server->focused_window_id != message->pointer.mouse_window_id && | |||
230 | server->pointer_grab_window_id == -1) | |||
231 | { | |||
232 | broadway_server_window_raise (server, message->pointer.mouse_window_id); | |||
233 | broadway_server_focus_window (server, message->pointer.mouse_window_id); | |||
234 | broadway_server_flush (server); | |||
235 | } | |||
236 | ||||
237 | server->last_x = message->pointer.root_x; | |||
238 | server->last_y = message->pointer.root_y; | |||
239 | server->last_state = message->pointer.state; | |||
240 | server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; | |||
241 | break; | |||
242 | case BROADWAY_EVENT_SCROLL: | |||
243 | server->last_x = message->pointer.root_x; | |||
244 | server->last_y = message->pointer.root_y; | |||
245 | server->last_state = message->pointer.state; | |||
246 | server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id; | |||
247 | break; | |||
248 | case BROADWAY_EVENT_TOUCH: | |||
249 | if (message->touch.touch_type == 0 && message->touch.is_emulated && | |||
250 | server->focused_window_id != message->touch.event_window_id) | |||
251 | { | |||
252 | broadway_server_window_raise (server, message->touch.event_window_id); | |||
253 | broadway_server_focus_window (server, message->touch.event_window_id); | |||
254 | broadway_server_flush (server); | |||
255 | } | |||
256 | ||||
257 | if (message->touch.is_emulated) | |||
258 | { | |||
259 | server->last_x = message->pointer.root_x; | |||
260 | server->last_y = message->pointer.root_y; | |||
261 | } | |||
262 | ||||
263 | server->last_state = message->touch.state; | |||
264 | break; | |||
265 | case BROADWAY_EVENT_KEY_PRESS: | |||
266 | case BROADWAY_EVENT_KEY_RELEASE: | |||
267 | server->last_state = message->key.state; | |||
268 | break; | |||
269 | case BROADWAY_EVENT_GRAB_NOTIFY: | |||
270 | case BROADWAY_EVENT_UNGRAB_NOTIFY: | |||
271 | break; | |||
272 | case BROADWAY_EVENT_CONFIGURE_NOTIFY: | |||
273 | window = g_hash_table_lookup (server->id_ht, | |||
274 | GINT_TO_POINTER (message->configure_notify.id)((gpointer) (glong) (message->configure_notify.id))); | |||
275 | if (window != NULL((void*)0)) | |||
276 | { | |||
277 | window->x = message->configure_notify.x; | |||
278 | window->y = message->configure_notify.y; | |||
279 | } | |||
280 | break; | |||
281 | case BROADWAY_EVENT_DELETE_NOTIFY: | |||
282 | break; | |||
283 | case BROADWAY_EVENT_SCREEN_SIZE_CHANGED: | |||
284 | server->root->width = message->screen_resize_notify.width; | |||
285 | server->root->height = message->screen_resize_notify.height; | |||
286 | break; | |||
287 | ||||
288 | default: | |||
289 | g_printerr ("update_event_state - Unknown input command %c\n", message->base.type); | |||
290 | break; | |||
291 | } | |||
292 | } | |||
293 | ||||
294 | gboolean | |||
295 | broadway_server_lookahead_event (BroadwayServer *server, | |||
296 | const char *types) | |||
297 | { | |||
298 | GList *l; | |||
299 | ||||
300 | for (l = server->input_messages; l != NULL((void*)0); l = l->next) | |||
301 | { | |||
302 | BroadwayInputMsg *message; | |||
303 | ||||
304 | message = l->data; | |||
305 | if (strchr (types, message->base.type) != NULL((void*)0)) | |||
306 | return TRUE(!(0)); | |||
307 | } | |||
308 | ||||
309 | return FALSE(0); | |||
310 | } | |||
311 | ||||
312 | static gboolean | |||
313 | is_pointer_event (BroadwayInputMsg *message) | |||
314 | { | |||
315 | return | |||
316 | message->base.type == BROADWAY_EVENT_ENTER || | |||
317 | message->base.type == BROADWAY_EVENT_LEAVE || | |||
318 | message->base.type == BROADWAY_EVENT_POINTER_MOVE || | |||
319 | message->base.type == BROADWAY_EVENT_BUTTON_PRESS || | |||
320 | message->base.type == BROADWAY_EVENT_BUTTON_RELEASE || | |||
321 | message->base.type == BROADWAY_EVENT_SCROLL || | |||
322 | message->base.type == BROADWAY_EVENT_GRAB_NOTIFY || | |||
323 | message->base.type == BROADWAY_EVENT_UNGRAB_NOTIFY; | |||
324 | } | |||
325 | ||||
326 | static void | |||
327 | process_input_message (BroadwayServer *server, | |||
328 | BroadwayInputMsg *message) | |||
329 | { | |||
330 | gint32 client; | |||
331 | ||||
332 | update_event_state (server, message); | |||
333 | client = -1; | |||
334 | if (is_pointer_event (message) && | |||
335 | server->pointer_grab_window_id != -1) | |||
336 | client = server->pointer_grab_client_id; | |||
337 | ||||
338 | broadway_events_got_input (message, client); | |||
339 | } | |||
340 | ||||
341 | static void | |||
342 | process_input_messages (BroadwayServer *server) | |||
343 | { | |||
344 | while (server->input_messages) | |||
345 | { | |||
346 | BroadwayInputMsg *message; | |||
347 | ||||
348 | message = server->input_messages->data; | |||
349 | server->input_messages = | |||
350 | g_list_delete_link (server->input_messages, | |||
351 | server->input_messages); | |||
352 | ||||
353 | if (message->base.serial == 0) | |||
354 | { | |||
355 | /* This was sent before we got any requests, but we don't want the | |||
356 | daemon serials to go backwards, so we fix it up to be the last used | |||
357 | serial */ | |||
358 | message->base.serial = server->saved_serial - 1; | |||
359 | } | |||
360 | ||||
361 | process_input_message (server, message); | |||
362 | g_free (message); | |||
363 | } | |||
364 | } | |||
365 | ||||
366 | static void | |||
367 | fake_configure_notify (BroadwayServer *server, | |||
368 | BroadwayWindow *window) | |||
369 | { | |||
370 | BroadwayInputMsg ev = { {0} }; | |||
371 | ||||
372 | ev.base.type = BROADWAY_EVENT_CONFIGURE_NOTIFY; | |||
373 | ev.base.serial = server->saved_serial - 1; | |||
374 | ev.base.time = server->last_seen_time; | |||
375 | ev.configure_notify.id = window->id; | |||
376 | ev.configure_notify.x = window->x; | |||
377 | ev.configure_notify.y = window->y; | |||
378 | ev.configure_notify.width = window->width; | |||
379 | ev.configure_notify.height = window->height; | |||
380 | ||||
381 | process_input_message (server, &ev); | |||
382 | } | |||
383 | ||||
384 | static guint32 * | |||
385 | parse_pointer_data (guint32 *p, BroadwayInputPointerMsg *data) | |||
386 | { | |||
387 | data->mouse_window_id = ntohl (*p++); | |||
388 | data->event_window_id = ntohl (*p++); | |||
389 | data->root_x = ntohl (*p++); | |||
390 | data->root_y = ntohl (*p++); | |||
391 | data->win_x = ntohl (*p++); | |||
392 | data->win_y = ntohl (*p++); | |||
393 | data->state = ntohl (*p++); | |||
394 | ||||
395 | return p; | |||
396 | } | |||
397 | ||||
398 | static guint32 * | |||
399 | parse_touch_data (guint32 *p, BroadwayInputTouchMsg *data) | |||
400 | { | |||
401 | data->touch_type = ntohl (*p++); | |||
402 | data->event_window_id = ntohl (*p++); | |||
403 | data->sequence_id = ntohl (*p++); | |||
404 | data->is_emulated = ntohl (*p++); | |||
405 | data->root_x = ntohl (*p++); | |||
406 | data->root_y = ntohl (*p++); | |||
407 | data->win_x = ntohl (*p++); | |||
408 | data->win_y = ntohl (*p++); | |||
409 | data->state = ntohl (*p++); | |||
410 | ||||
411 | return p; | |||
412 | } | |||
413 | ||||
414 | static void | |||
415 | update_future_pointer_info (BroadwayServer *server, BroadwayInputPointerMsg *data) | |||
416 | { | |||
417 | server->future_root_x = data->root_x; | |||
418 | server->future_root_y = data->root_y; | |||
419 | server->future_state = data->state; | |||
420 | server->future_mouse_in_toplevel = data->mouse_window_id; | |||
421 | } | |||
422 | ||||
423 | static void | |||
424 | parse_input_message (BroadwayInput *input, const unsigned char *message) | |||
425 | { | |||
426 | BroadwayServer *server = input->server; | |||
427 | BroadwayInputMsg msg; | |||
428 | guint32 *p; | |||
429 | gint64 time_; | |||
430 | ||||
431 | memset (&msg, 0, sizeof (msg)); | |||
432 | ||||
433 | p = (guint32 *) message; | |||
434 | ||||
435 | msg.base.type = ntohl (*p++); | |||
436 | msg.base.serial = ntohl (*p++); | |||
437 | time_ = ntohl (*p++); | |||
438 | ||||
439 | if (time_ == 0) { | |||
440 | time_ = server->last_seen_time; | |||
441 | } else { | |||
442 | if (!input->seen_time) { | |||
443 | input->seen_time = TRUE(!(0)); | |||
444 | /* Calculate time base so that any following times are normalized to start | |||
445 | 5 seconds after last_seen_time, to avoid issues that could appear when | |||
446 | a long hiatus due to a reconnect seems to be instant */ | |||
447 | input->time_base = time_ - (server->last_seen_time + 5000); | |||
448 | } | |||
449 | time_ = time_ - input->time_base; | |||
450 | } | |||
451 | ||||
452 | server->last_seen_time = time_; | |||
453 | ||||
454 | msg.base.time = time_; | |||
455 | ||||
456 | switch (msg.base.type) { | |||
457 | case BROADWAY_EVENT_ENTER: | |||
458 | case BROADWAY_EVENT_LEAVE: | |||
459 | p = parse_pointer_data (p, &msg.pointer); | |||
460 | update_future_pointer_info (server, &msg.pointer); | |||
461 | msg.crossing.mode = ntohl (*p++); | |||
462 | break; | |||
463 | ||||
464 | case BROADWAY_EVENT_POINTER_MOVE: /* Mouse move */ | |||
465 | p = parse_pointer_data (p, &msg.pointer); | |||
466 | update_future_pointer_info (server, &msg.pointer); | |||
467 | break; | |||
468 | ||||
469 | case BROADWAY_EVENT_BUTTON_PRESS: | |||
470 | case BROADWAY_EVENT_BUTTON_RELEASE: | |||
471 | p = parse_pointer_data (p, &msg.pointer); | |||
472 | update_future_pointer_info (server, &msg.pointer); | |||
473 | msg.button.button = ntohl (*p++); | |||
474 | break; | |||
475 | ||||
476 | case BROADWAY_EVENT_SCROLL: | |||
477 | p = parse_pointer_data (p, &msg.pointer); | |||
478 | update_future_pointer_info (server, &msg.pointer); | |||
479 | msg.scroll.dir = ntohl (*p++); | |||
480 | break; | |||
481 | ||||
482 | case BROADWAY_EVENT_TOUCH: | |||
483 | p = parse_touch_data (p, &msg.touch); | |||
484 | break; | |||
485 | ||||
486 | case BROADWAY_EVENT_KEY_PRESS: | |||
487 | case BROADWAY_EVENT_KEY_RELEASE: | |||
488 | msg.key.window_id = server->focused_window_id; | |||
489 | msg.key.key = ntohl (*p++); | |||
490 | msg.key.state = ntohl (*p++); | |||
491 | break; | |||
492 | ||||
493 | case BROADWAY_EVENT_GRAB_NOTIFY: | |||
494 | case BROADWAY_EVENT_UNGRAB_NOTIFY: | |||
495 | msg.grab_reply.res = ntohl (*p++); | |||
496 | break; | |||
497 | ||||
498 | case BROADWAY_EVENT_CONFIGURE_NOTIFY: | |||
499 | msg.configure_notify.id = ntohl (*p++); | |||
500 | msg.configure_notify.x = ntohl (*p++); | |||
501 | msg.configure_notify.y = ntohl (*p++); | |||
502 | msg.configure_notify.width = ntohl (*p++); | |||
503 | msg.configure_notify.height = ntohl (*p++); | |||
504 | break; | |||
505 | ||||
506 | case BROADWAY_EVENT_DELETE_NOTIFY: | |||
507 | msg.delete_notify.id = ntohl (*p++); | |||
508 | break; | |||
509 | ||||
510 | case BROADWAY_EVENT_SCREEN_SIZE_CHANGED: | |||
511 | msg.screen_resize_notify.width = ntohl (*p++); | |||
512 | msg.screen_resize_notify.height = ntohl (*p++); | |||
513 | break; | |||
514 | ||||
515 | default: | |||
516 | g_printerr ("parse_input_message - Unknown input command %c (%s)\n", msg.base.type, message); | |||
517 | break; | |||
518 | } | |||
519 | ||||
520 | server->input_messages = g_list_append (server->input_messages, g_memdup2 (&msg, sizeof (msg))); | |||
521 | ||||
522 | } | |||
523 | ||||
524 | static inline void | |||
525 | hex_dump (guchar *data G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
526 | gsize len G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
527 | { | |||
528 | #ifdef DEBUG_WEBSOCKETS | |||
529 | gsize i, j; | |||
530 | for (j = 0; j < len + 15; j += 16) | |||
531 | { | |||
532 | fprintf (stderrstderr, "0x%.4x ", j); | |||
533 | for (i = 0; i < 16; i++) | |||
534 | { | |||
535 | if ((j + i) < len) | |||
536 | fprintf (stderrstderr, "%.2x ", data[j+i]); | |||
537 | else | |||
538 | fprintf (stderrstderr, " "); | |||
539 | if (i == 8) | |||
540 | fprintf (stderrstderr, " "); | |||
541 | } | |||
542 | fprintf (stderrstderr, " | "); | |||
543 | ||||
544 | for (i = 0; i < 16; i++) | |||
545 | if ((j + i) < len && g_ascii_isalnum(data[j+i])((g_ascii_table[(guchar) (data[j+i])] & G_ASCII_ALNUM) != 0)) | |||
546 | fprintf (stderrstderr, "%c", data[j+i]); | |||
547 | else | |||
548 | fprintf (stderrstderr, "."); | |||
549 | fprintf (stderrstderr, "\n"); | |||
550 | } | |||
551 | #endif | |||
552 | } | |||
553 | ||||
554 | static void | |||
555 | parse_input (BroadwayInput *input) | |||
556 | { | |||
557 | if (!input->buffer->len) | |||
558 | return; | |||
559 | ||||
560 | hex_dump (input->buffer->data, input->buffer->len); | |||
561 | ||||
562 | while (input->buffer->len > 2) | |||
563 | { | |||
564 | gsize len, payload_len; | |||
565 | BroadwayWSOpCode code; | |||
566 | gboolean is_mask, fin; | |||
567 | guchar *buf, *data, *mask; | |||
568 | ||||
569 | buf = input->buffer->data; | |||
570 | len = input->buffer->len; | |||
571 | ||||
572 | #ifdef DEBUG_WEBSOCKETS | |||
573 | g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]); | |||
574 | #endif | |||
575 | ||||
576 | fin = buf[0] & 0x80; | |||
577 | code = buf[0] & 0x0f; | |||
578 | payload_len = buf[1] & 0x7f; | |||
579 | is_mask = buf[1] & 0x80; | |||
580 | data = buf + 2; | |||
581 | ||||
582 | if (payload_len == 126) | |||
583 | { | |||
584 | if (len < 4) | |||
585 | return; | |||
586 | payload_len = GUINT16_FROM_BE( *(guint16 *) data )(((((guint16) ( (guint16) ((guint16) (*(guint16 *) data) >> 8) | (guint16) ((guint16) (*(guint16 *) data) << 8)))) )); | |||
587 | data += 2; | |||
588 | } | |||
589 | else if (payload_len == 127) | |||
590 | { | |||
591 | if (len < 10) | |||
592 | return; | |||
593 | payload_len = GUINT64_FROM_BE( *(guint64 *) data )(((((guint64) ( (((guint64) (*(guint64 *) data) & (guint64 ) (0x00000000000000ffUL)) << 56) | (((guint64) (*(guint64 *) data) & (guint64) (0x000000000000ff00UL)) << 40 ) | (((guint64) (*(guint64 *) data) & (guint64) (0x0000000000ff0000UL )) << 24) | (((guint64) (*(guint64 *) data) & (guint64 ) (0x00000000ff000000UL)) << 8) | (((guint64) (*(guint64 *) data) & (guint64) (0x000000ff00000000UL)) >> 8) | (((guint64) (*(guint64 *) data) & (guint64) (0x0000ff0000000000UL )) >> 24) | (((guint64) (*(guint64 *) data) & (guint64 ) (0x00ff000000000000UL)) >> 40) | (((guint64) (*(guint64 *) data) & (guint64) (0xff00000000000000UL)) >> 56 )))))); | |||
594 | data += 8; | |||
595 | } | |||
596 | ||||
597 | mask = NULL((void*)0); | |||
598 | if (is_mask) | |||
599 | { | |||
600 | if (data - buf + 4 > len) | |||
601 | return; | |||
602 | mask = data; | |||
603 | data += 4; | |||
604 | } | |||
605 | ||||
606 | if (data - buf + payload_len > len) | |||
607 | return; /* wait to accumulate more */ | |||
608 | ||||
609 | if (is_mask) | |||
610 | { | |||
611 | gsize i; | |||
612 | for (i = 0; i < payload_len; i++) | |||
613 | data[i] ^= mask[i%4]; | |||
614 | } | |||
615 | ||||
616 | switch (code) { | |||
617 | case BROADWAY_WS_CNX_CLOSE: | |||
618 | break; /* hang around anyway */ | |||
619 | case BROADWAY_WS_BINARY: | |||
620 | if (!fin) | |||
621 | { | |||
622 | #ifdef DEBUG_WEBSOCKETS | |||
623 | g_warning ("can't yet accept fragmented input"); | |||
624 | #endif | |||
625 | } | |||
626 | else | |||
627 | { | |||
628 | parse_input_message (input, data); | |||
629 | } | |||
630 | break; | |||
631 | case BROADWAY_WS_CNX_PING: | |||
632 | broadway_output_pong (input->output); | |||
633 | break; | |||
634 | case BROADWAY_WS_CNX_PONG: | |||
635 | break; /* we never send pings, but tolerate pongs */ | |||
636 | case BROADWAY_WS_TEXT: | |||
637 | case BROADWAY_WS_CONTINUATION: | |||
638 | default: | |||
639 | { | |||
640 | g_warning ("fragmented or unknown input code 0x%2x with fin set", code); | |||
641 | break; | |||
642 | } | |||
643 | } | |||
644 | ||||
645 | g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len); | |||
646 | } | |||
647 | } | |||
648 | ||||
649 | ||||
650 | static gboolean | |||
651 | process_input_idle_cb (BroadwayServer *server) | |||
652 | { | |||
653 | server->process_input_idle = 0; | |||
654 | process_input_messages (server); | |||
655 | return G_SOURCE_REMOVE(0); | |||
656 | } | |||
657 | ||||
658 | static void | |||
659 | queue_process_input_at_idle (BroadwayServer *server) | |||
660 | { | |||
661 | if (server->process_input_idle == 0) | |||
662 | server->process_input_idle = | |||
663 | g_idle_add_full (G_PRIORITY_DEFAULT0, (GSourceFunc)process_input_idle_cb, server, NULL((void*)0)); | |||
664 | } | |||
665 | ||||
666 | static gboolean | |||
667 | broadway_server_read_all_input_nonblocking (BroadwayInput *input) | |||
668 | { | |||
669 | GInputStream *in; | |||
670 | gssize res; | |||
671 | guint8 buffer[1024]; | |||
672 | GError *error = NULL((void*)0); | |||
673 | ||||
674 | if (input == NULL((void*)0)) | |||
675 | return FALSE(0); | |||
676 | ||||
677 | in = g_io_stream_get_input_stream (input->connection); | |||
678 | ||||
679 | res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in)((((GPollableInputStream*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((in)), ((g_pollable_input_stream_get_type ())))))), | |||
680 | buffer, sizeof (buffer), NULL((void*)0), &error); | |||
681 | ||||
682 | if (res <= 0) | |||
683 | { | |||
684 | if (res < 0 && | |||
685 | g_error_matches (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_WOULD_BLOCK)) | |||
686 | { | |||
687 | g_error_free (error); | |||
688 | return TRUE(!(0)); | |||
689 | } | |||
690 | ||||
691 | if (input->server->input == input) | |||
692 | input->server->input = NULL((void*)0); | |||
693 | broadway_input_free (input); | |||
694 | if (res < 0) | |||
695 | { | |||
696 | g_printerr ("input error %s\n", error->message); | |||
697 | g_error_free (error); | |||
698 | } | |||
699 | return FALSE(0); | |||
700 | } | |||
701 | ||||
702 | g_byte_array_append (input->buffer, buffer, res); | |||
703 | ||||
704 | parse_input (input); | |||
705 | return TRUE(!(0)); | |||
706 | } | |||
707 | ||||
708 | static void | |||
709 | broadway_server_consume_all_input (BroadwayServer *server) | |||
710 | { | |||
711 | broadway_server_read_all_input_nonblocking (server->input); | |||
712 | ||||
713 | /* Since we're parsing input but not processing the resulting messages | |||
714 | we might not get a readable callback on the stream, so queue an idle to | |||
715 | process the messages */ | |||
716 | queue_process_input_at_idle (server); | |||
717 | } | |||
718 | ||||
719 | ||||
720 | static gboolean | |||
721 | input_data_cb (GObject *stream G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
722 | BroadwayInput *input) | |||
723 | { | |||
724 | BroadwayServer *server = input->server; | |||
725 | ||||
726 | if (!broadway_server_read_all_input_nonblocking (input)) | |||
727 | return FALSE(0); | |||
728 | ||||
729 | if (input->active) | |||
730 | process_input_messages (server); | |||
731 | ||||
732 | return TRUE(!(0)); | |||
733 | } | |||
734 | ||||
735 | guint32 | |||
736 | broadway_server_get_next_serial (BroadwayServer *server) | |||
737 | { | |||
738 | if (server->output) | |||
739 | return broadway_output_get_next_serial (server->output); | |||
740 | ||||
741 | return server->saved_serial; | |||
742 | } | |||
743 | ||||
744 | void | |||
745 | broadway_server_get_screen_size (BroadwayServer *server, | |||
746 | guint32 *width, | |||
747 | guint32 *height) | |||
748 | { | |||
749 | *width = server->root->width; | |||
750 | *height = server->root->height; | |||
751 | } | |||
752 | ||||
753 | ||||
754 | void | |||
755 | broadway_server_flush (BroadwayServer *server) | |||
756 | { | |||
757 | if (server->output && | |||
758 | !broadway_output_flush (server->output)) | |||
759 | { | |||
760 | server->saved_serial = broadway_output_get_next_serial (server->output); | |||
761 | broadway_output_free (server->output); | |||
762 | server->output = NULL((void*)0); | |||
763 | } | |||
764 | } | |||
765 | ||||
766 | #if 0 | |||
767 | /* TODO: This is not used atm, is it needed? */ | |||
768 | /* Note: This may be called while handling a message (i.e. sorta recursively) */ | |||
769 | static BroadwayInputMsg * | |||
770 | broadway_server_block_for_input (BroadwayServer *server, char op, | |||
771 | guint32 serial, gboolean remove_message) | |||
772 | { | |||
773 | BroadwayInputMsg *message; | |||
774 | gssize res; | |||
775 | guint8 buffer[1024]; | |||
776 | BroadwayInput *input; | |||
777 | GInputStream *in; | |||
778 | GList *l; | |||
779 | ||||
780 | broadway_server_flush (server); | |||
781 | ||||
782 | if (server->input == NULL((void*)0)) | |||
783 | return NULL((void*)0); | |||
784 | ||||
785 | input = server->input; | |||
786 | ||||
787 | while (TRUE(!(0))) { | |||
788 | /* Check for existing reply in queue */ | |||
789 | ||||
790 | for (l = server->input_messages; l != NULL((void*)0); l = l->next) | |||
791 | { | |||
792 | message = l->data; | |||
793 | ||||
794 | if (message->base.type == op) | |||
795 | { | |||
796 | if (message->base.serial == serial) | |||
797 | { | |||
798 | if (remove_message) | |||
799 | server->input_messages = | |||
800 | g_list_delete_link (server->input_messages, l); | |||
801 | return message; | |||
802 | } | |||
803 | } | |||
804 | } | |||
805 | ||||
806 | /* Not found, read more, blocking */ | |||
807 | ||||
808 | in = g_io_stream_get_input_stream (input->connection); | |||
809 | ||||
810 | res = g_input_stream_read (in, buffer, sizeof (buffer), NULL((void*)0), NULL((void*)0)); | |||
811 | if (res <= 0) | |||
812 | return NULL((void*)0); | |||
813 | g_byte_array_append (input->buffer, buffer, res); | |||
814 | ||||
815 | parse_input (input); | |||
816 | ||||
817 | /* Since we're parsing input but not processing the resulting messages | |||
818 | we might not get a readable callback on the stream, so queue an idle to | |||
819 | process the messages */ | |||
820 | queue_process_input_at_idle (server); | |||
821 | } | |||
822 | } | |||
823 | #endif | |||
824 | ||||
825 | static void * | |||
826 | map_named_shm (char *name, gsize size) | |||
827 | { | |||
828 | #ifdef G_OS_UNIX | |||
829 | ||||
830 | int fd; | |||
831 | void *ptr; | |||
832 | char *filename = NULL((void*)0); | |||
833 | ||||
834 | fd = shm_open (name, O_RDONLY00, 0600); | |||
835 | if (fd == -1) | |||
836 | { | |||
837 | filename = g_build_filename (g_get_tmp_dir (), name, NULL((void*)0)); | |||
838 | fd = open (filename, O_RDONLY00); | |||
839 | if (fd == -1) | |||
840 | { | |||
841 | perror ("Failed to map shm"); | |||
842 | g_free (filename); | |||
843 | ||||
844 | return NULL((void*)0); | |||
845 | } | |||
846 | } | |||
847 | ||||
848 | ptr = mmap (0, size, PROT_READ0x1, MAP_SHARED0x01, fd, 0); | |||
849 | ||||
850 | (void) close (fd); | |||
851 | ||||
852 | if (filename) | |||
853 | { | |||
854 | unlink (filename); | |||
855 | g_free (filename); | |||
856 | } | |||
857 | else | |||
858 | shm_unlink (name); | |||
859 | ||||
860 | return ptr; | |||
861 | ||||
862 | #elif defined(G_OS_WIN32) | |||
863 | ||||
864 | int fd; | |||
865 | void *ptr; | |||
866 | char *shmpath; | |||
867 | void *map = ((void *)-1); | |||
868 | ||||
869 | if (*name == '/') | |||
870 | ++name; | |||
871 | shmpath = g_build_filename (g_get_tmp_dir (), name, NULL((void*)0)); | |||
872 | ||||
873 | fd = open(shmpath, O_RDONLY00, 0600); | |||
874 | if (fd == -1) | |||
875 | { | |||
876 | g_free (shmpath); | |||
877 | perror ("Failed to shm_open"); | |||
878 | return NULL((void*)0); | |||
879 | } | |||
880 | ||||
881 | if (size == 0) | |||
882 | ptr = map; | |||
883 | else | |||
884 | { | |||
885 | HANDLE h, fm; | |||
886 | h = (HANDLE)_get_osfhandle (fd); | |||
887 | fm = CreateFileMapping (h, NULL((void*)0), PAGE_READONLY, 0, (DWORD)size, NULL((void*)0)); | |||
888 | ptr = MapViewOfFile (fm, FILE_MAP_READ, 0, 0, (size_t)size); | |||
889 | CloseHandle (fm); | |||
890 | } | |||
891 | ||||
892 | (void) close(fd); | |||
893 | ||||
894 | remove (shmpath); | |||
895 | g_free (shmpath); | |||
896 | ||||
897 | return ptr; | |||
898 | ||||
899 | #else | |||
900 | #error "No shm mapping supported" | |||
901 | ||||
902 | return NULL((void*)0); | |||
903 | #endif | |||
904 | } | |||
905 | ||||
906 | static char * | |||
907 | parse_line (char *line, char *key) | |||
908 | { | |||
909 | char *p; | |||
910 | ||||
911 | if (!g_str_has_prefix (line, key)(__builtin_constant_p (key)? __extension__ ({ const char * const __str = (line); const char * const __prefix = (key); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void *)0)) __result = (g_str_has_prefix) (__str, __prefix); else { const size_t __str_len = strlen (((__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0; } __result; }) : (g_str_has_prefix) (line, key) )) | |||
912 | return NULL((void*)0); | |||
913 | p = line + strlen (key); | |||
914 | if (*p != ':') | |||
915 | return NULL((void*)0); | |||
916 | p++; | |||
917 | /* Skip optional initial space */ | |||
918 | if (*p == ' ') | |||
919 | p++; | |||
920 | return p; | |||
921 | } | |||
922 | ||||
923 | static void | |||
924 | send_error (HttpRequest *request, | |||
925 | int error_code, | |||
926 | const char *reason) | |||
927 | { | |||
928 | char *res; | |||
929 | ||||
930 | res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n" | |||
931 | "<html><head><title>%d %s</title></head>" | |||
932 | "<body>%s</body></html>", | |||
933 | error_code, reason, | |||
934 | error_code, reason, | |||
935 | reason); | |||
936 | ||||
937 | /* TODO: This should really be async */ | |||
938 | g_output_stream_write_all (g_io_stream_get_output_stream (request->connection), | |||
939 | res, strlen (res), NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
940 | ||||
941 | g_free (res); | |||
942 | http_request_free (request); | |||
943 | } | |||
944 | ||||
945 | /* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */ | |||
946 | #define SEC_WEB_SOCKET_KEY_MAGIC"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | |||
947 | ||||
948 | /* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */ | |||
949 | static gchar * | |||
950 | generate_handshake_response_wsietf_v7 (const gchar *key) | |||
951 | { | |||
952 | gsize digest_len = 20; | |||
953 | guchar digest[20]; | |||
954 | GChecksum *checksum; | |||
955 | ||||
956 | checksum = g_checksum_new (G_CHECKSUM_SHA1); | |||
957 | if (!checksum) | |||
958 | return NULL((void*)0); | |||
959 | ||||
960 | g_checksum_update (checksum, (guchar *)key, -1); | |||
961 | g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC"258EAFA5-E914-47DA-95CA-C5AB0DC85B11", -1); | |||
962 | ||||
963 | g_checksum_get_digest (checksum, digest, &digest_len); | |||
964 | g_checksum_free (checksum); | |||
965 | ||||
966 | g_assert (digest_len == 20)do { if (digest_len == 20) ; else g_assertion_message_expr ("Cdk" , "broadway-server.c", 966, ((const char*) (__func__)), "digest_len == 20" ); } while (0); | |||
967 | ||||
968 | return g_base64_encode (digest, digest_len); | |||
969 | } | |||
970 | ||||
971 | static void | |||
972 | start_input (HttpRequest *request) | |||
973 | { | |||
974 | char **lines; | |||
975 | char *p; | |||
976 | int i; | |||
977 | char *res; | |||
978 | char *origin, *host; | |||
979 | BroadwayInput *input; | |||
980 | const void *data_buffer; | |||
981 | gsize data_buffer_size; | |||
982 | GInputStream *in; | |||
983 | char *key; | |||
984 | GSocket *socket; | |||
985 | int flag = 1; | |||
986 | ||||
987 | #ifdef DEBUG_WEBSOCKETS | |||
988 | g_print ("incoming request:\n%s\n", request->request->str); | |||
989 | #endif | |||
990 | lines = g_strsplit (request->request->str, "\n", 0); | |||
991 | ||||
992 | key = NULL((void*)0); | |||
993 | origin = NULL((void*)0); | |||
994 | host = NULL((void*)0); | |||
995 | for (i = 0; lines[i] != NULL((void*)0); i++) | |||
996 | { | |||
997 | if ((p = parse_line (lines[i], "Sec-WebSocket-Key"))) | |||
998 | key = p; | |||
999 | else if ((p = parse_line (lines[i], "Origin"))) | |||
1000 | origin = p; | |||
1001 | else if ((p = parse_line (lines[i], "Host"))) | |||
1002 | host = p; | |||
1003 | else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin"))) | |||
1004 | origin = p; | |||
1005 | } | |||
1006 | ||||
1007 | if (host == NULL((void*)0)) | |||
1008 | { | |||
1009 | g_strfreev (lines); | |||
1010 | send_error (request, 400, "Bad websocket request"); | |||
1011 | return; | |||
1012 | } | |||
1013 | ||||
1014 | if (key != NULL((void*)0)) | |||
1015 | { | |||
1016 | char* accept = generate_handshake_response_wsietf_v7 (key); | |||
1017 | res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n" | |||
1018 | "Upgrade: websocket\r\n" | |||
1019 | "Connection: Upgrade\r\n" | |||
1020 | "Sec-WebSocket-Accept: %s\r\n" | |||
1021 | "%s%s%s" | |||
1022 | "Sec-WebSocket-Location: ws://%s/socket\r\n" | |||
1023 | "Sec-WebSocket-Protocol: broadway\r\n" | |||
1024 | "\r\n", accept, | |||
1025 | origin?"Sec-WebSocket-Origin: ":"", origin?origin:"", origin?"\r\n":"", | |||
1026 | host); | |||
1027 | g_free (accept); | |||
1028 | ||||
1029 | #ifdef DEBUG_WEBSOCKETS | |||
1030 | g_print ("v7 proto response:\n%s", res); | |||
1031 | #endif | |||
1032 | ||||
1033 | g_output_stream_write_all (g_io_stream_get_output_stream (request->connection), | |||
1034 | res, strlen (res), NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
1035 | g_free (res); | |||
1036 | } | |||
1037 | else | |||
1038 | { | |||
1039 | g_strfreev (lines); | |||
1040 | send_error (request, 400, "Bad websocket request"); | |||
1041 | return; | |||
1042 | } | |||
1043 | ||||
1044 | socket = g_socket_connection_get_socket (request->socket_connection); | |||
1045 | setsockopt (g_socket_get_fd (socket), IPPROTO_TCPIPPROTO_TCP, | |||
1046 | TCP_NODELAY1, (char *) &flag, sizeof(int)); | |||
1047 | ||||
1048 | input = g_new0 (BroadwayInput, 1)((BroadwayInput *) g_malloc0_n ((1), sizeof (BroadwayInput))); | |||
1049 | input->server = request->server; | |||
1050 | input->connection = g_object_ref (request->connection)((__typeof__ (request->connection)) (g_object_ref) (request ->connection)); | |||
1051 | ||||
1052 | data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data)((((GBufferedInputStream*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((request->data)), ((g_buffered_input_stream_get_type ())))))), &data_buffer_size); | |||
1053 | input->buffer = g_byte_array_sized_new (data_buffer_size); | |||
1054 | g_byte_array_append (input->buffer, data_buffer, data_buffer_size); | |||
1055 | ||||
1056 | input->output = | |||
1057 | broadway_output_new (g_io_stream_get_output_stream (request->connection), 0); | |||
1058 | ||||
1059 | /* This will free and close the data input stream, but we got all the buffered content already */ | |||
1060 | http_request_free (request); | |||
1061 | ||||
1062 | in = g_io_stream_get_input_stream (input->connection); | |||
1063 | ||||
1064 | input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in)((((GPollableInputStream*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((in)), ((g_pollable_input_stream_get_type ())))))), NULL((void*)0)); | |||
1065 | g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL((void*)0)); | |||
1066 | g_source_attach (input->source, NULL((void*)0)); | |||
1067 | ||||
1068 | start (input); | |||
1069 | ||||
1070 | /* Process any data in the pipe already */ | |||
1071 | parse_input (input); | |||
1072 | ||||
1073 | g_strfreev (lines); | |||
1074 | } | |||
1075 | ||||
1076 | static void | |||
1077 | start (BroadwayInput *input) | |||
1078 | { | |||
1079 | BroadwayServer *server; | |||
1080 | ||||
1081 | input->active = TRUE(!(0)); | |||
1082 | ||||
1083 | server = BROADWAY_SERVER (input->server)((((BroadwayServer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((input->server)), ((broadway_server_get_type())))))); | |||
1084 | ||||
1085 | if (server->output) | |||
1086 | { | |||
1087 | broadway_output_disconnected (server->output); | |||
1088 | broadway_output_flush (server->output); | |||
1089 | } | |||
1090 | ||||
1091 | if (server->input != NULL((void*)0)) | |||
1092 | { | |||
1093 | broadway_input_free (server->input); | |||
1094 | server->input = NULL((void*)0); | |||
1095 | } | |||
1096 | ||||
1097 | server->input = input; | |||
1098 | ||||
1099 | if (server->output) | |||
1100 | { | |||
1101 | server->saved_serial = broadway_output_get_next_serial (server->output); | |||
1102 | broadway_output_free (server->output); | |||
1103 | } | |||
1104 | server->output = input->output; | |||
1105 | ||||
1106 | broadway_output_set_next_serial (server->output, server->saved_serial); | |||
1107 | broadway_output_flush (server->output); | |||
1108 | ||||
1109 | broadway_server_resync_windows (server); | |||
1110 | ||||
1111 | if (server->pointer_grab_window_id != -1) | |||
1112 | broadway_output_grab_pointer (server->output, | |||
1113 | server->pointer_grab_window_id, | |||
1114 | server->pointer_grab_owner_events); | |||
1115 | ||||
1116 | process_input_messages (server); | |||
1117 | } | |||
1118 | ||||
1119 | static void | |||
1120 | send_data (HttpRequest *request, | |||
1121 | const char *mimetype, | |||
1122 | const char *data, gsize len) | |||
1123 | { | |||
1124 | char *res; | |||
1125 | ||||
1126 | res = g_strdup_printf ("HTTP/1.0 200 OK\r\n" | |||
1127 | "Content-Type: %s\r\n" | |||
1128 | "Content-Length: %"G_GSIZE_FORMAT"lu""\r\n" | |||
1129 | "\r\n", | |||
1130 | mimetype, len); | |||
1131 | ||||
1132 | /* TODO: This should really be async */ | |||
1133 | g_output_stream_write_all (g_io_stream_get_output_stream (request->connection), | |||
1134 | res, strlen (res), NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
1135 | g_free (res); | |||
1136 | g_output_stream_write_all (g_io_stream_get_output_stream (request->connection), | |||
1137 | data, len, NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
1138 | http_request_free (request); | |||
1139 | } | |||
1140 | ||||
1141 | #include "clienthtml.h" | |||
1142 | #include "broadwayjs.h" | |||
1143 | ||||
1144 | static void | |||
1145 | got_request (HttpRequest *request) | |||
1146 | { | |||
1147 | char *start, *escaped, *tmp, *version, *query; | |||
1148 | ||||
1149 | if (!g_str_has_prefix (request->request->str, "GET ")(__builtin_constant_p ("GET ")? __extension__ ({ const char * const __str = (request->request->str); const char * const __prefix = ("GET "); gboolean __result = (0); if (__str == ( (void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix ) (__str, __prefix); else { const size_t __str_len = strlen ( ((__str) + !(__str))); const size_t __prefix_len = strlen ((( __prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (request->request ->str, "GET ") )) | |||
1150 | { | |||
1151 | send_error (request, 501, "Only GET implemented"); | |||
1152 | return; | |||
1153 | } | |||
1154 | ||||
1155 | start = request->request->str + 4; /* Skip "GET " */ | |||
1156 | ||||
1157 | while (*start == ' ') | |||
1158 | start++; | |||
1159 | ||||
1160 | for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) | |||
1161 | ; | |||
1162 | escaped = g_strndup (start, tmp - start); | |||
1163 | version = NULL((void*)0); | |||
1164 | if (*tmp == ' ') | |||
1165 | { | |||
1166 | start = tmp; | |||
1167 | while (*start == ' ') | |||
1168 | start++; | |||
1169 | for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++) | |||
1170 | ; | |||
1171 | version = g_strndup (start, tmp - start); | |||
1172 | } | |||
1173 | ||||
1174 | query = strchr (escaped, '?'); | |||
1175 | if (query) | |||
1176 | *query = 0; | |||
1177 | ||||
1178 | if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0) | |||
1179 | send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html)(sizeof (client_html) / sizeof ((client_html)[0])) - 1); | |||
1180 | else if (strcmp (escaped, "/broadway.js") == 0) | |||
1181 | send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js)(sizeof (broadway_js) / sizeof ((broadway_js)[0])) - 1); | |||
1182 | else if (strcmp (escaped, "/socket") == 0) | |||
1183 | start_input (request); | |||
1184 | else | |||
1185 | send_error (request, 404, "File not found"); | |||
1186 | ||||
1187 | g_free (escaped); | |||
1188 | g_free (version); | |||
1189 | } | |||
1190 | ||||
1191 | static void | |||
1192 | got_http_request_line (GInputStream *stream, | |||
1193 | GAsyncResult *result, | |||
1194 | HttpRequest *request) | |||
1195 | { | |||
1196 | char *line; | |||
1197 | ||||
1198 | line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream)((((GDataInputStream*) (void *) g_type_check_instance_cast (( GTypeInstance*) ((stream)), ((g_data_input_stream_get_type () )))))), result, NULL((void*)0), NULL((void*)0)); | |||
1199 | if (line == NULL((void*)0)) | |||
1200 | { | |||
1201 | http_request_free (request); | |||
1202 | g_printerr ("Error reading request lines\n"); | |||
1203 | return; | |||
1204 | } | |||
1205 | if (strlen (line) == 0) | |||
1206 | got_request (request); | |||
1207 | else | |||
1208 | { | |||
1209 | /* Protect against overflow in request length */ | |||
1210 | if (request->request->len > 1024 * 5) | |||
1211 | { | |||
1212 | send_error (request, 400, "Request too long"); | |||
1213 | } | |||
1214 | else | |||
1215 | { | |||
1216 | g_string_append_printf (request->request, "%s\n", line); | |||
1217 | g_data_input_stream_read_line_async (request->data, 0, NULL((void*)0), | |||
1218 | (GAsyncReadyCallback)got_http_request_line, request); | |||
1219 | } | |||
1220 | } | |||
1221 | g_free (line); | |||
1222 | } | |||
1223 | ||||
1224 | static gboolean | |||
1225 | handle_incoming_connection (GSocketService *service G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1226 | GSocketConnection *connection, | |||
1227 | GObject *source_object) | |||
1228 | { | |||
1229 | HttpRequest *request; | |||
1230 | GInputStream *in; | |||
1231 | ||||
1232 | request = g_new0 (HttpRequest, 1)((HttpRequest *) g_malloc0_n ((1), sizeof (HttpRequest))); | |||
| ||||
1233 | request->socket_connection = g_object_ref (connection)((__typeof__ (connection)) (g_object_ref) (connection)); | |||
1234 | request->server = BROADWAY_SERVER (source_object)((((BroadwayServer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((source_object)), ((broadway_server_get_type())))))); | |||
1235 | request->request = g_string_new (""); | |||
1236 | ||||
1237 | if (request->server->ssl_cert && request->server->ssl_key) | |||
1238 | { | |||
1239 | GError *error = NULL((void*)0); | |||
1240 | GTlsCertificate *certificate; | |||
1241 | ||||
1242 | certificate = g_tls_certificate_new_from_files (request->server->ssl_cert, | |||
1243 | request->server->ssl_key, | |||
1244 | &error); | |||
1245 | if (!certificate) | |||
1246 | { | |||
1247 | g_warning ("Cannot create TLS certificate: %s", error->message); | |||
| ||||
1248 | g_error_free (error); | |||
1249 | return FALSE(0); | |||
1250 | } | |||
1251 | ||||
1252 | request->connection = g_tls_server_connection_new (G_IO_STREAM (connection)((((GIOStream*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((connection)), ((g_io_stream_get_type ())))))), | |||
1253 | certificate, | |||
1254 | &error); | |||
1255 | if (!request->connection) | |||
1256 | { | |||
1257 | g_warning ("Cannot create TLS connection: %s", error->message); | |||
1258 | g_error_free (error); | |||
1259 | return FALSE(0); | |||
1260 | } | |||
1261 | ||||
1262 | if (!g_tls_connection_handshake (G_TLS_CONNECTION (request->connection)((((GTlsConnection*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((request->connection)), ((g_tls_connection_get_type () )))))), | |||
1263 | NULL((void*)0), &error)) | |||
1264 | { | |||
1265 | g_warning ("Cannot create TLS connection: %s", error->message); | |||
1266 | g_error_free (error); | |||
1267 | return FALSE(0); | |||
1268 | } | |||
1269 | } | |||
1270 | else | |||
1271 | { | |||
1272 | request->connection = G_IO_STREAM (g_object_ref (connection))((((GIOStream*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((((__typeof__ (connection)) (g_object_ref) (connection))) ), ((g_io_stream_get_type ())))))); | |||
1273 | } | |||
1274 | ||||
1275 | in = g_io_stream_get_input_stream (request->connection); | |||
1276 | ||||
1277 | request->data = g_data_input_stream_new (in); | |||
1278 | g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data)((((GFilterInputStream*) (void *) g_type_check_instance_cast ( (GTypeInstance*) ((request->data)), ((g_filter_input_stream_get_type ())))))), FALSE(0)); | |||
1279 | /* Be tolerant of input */ | |||
1280 | g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY); | |||
1281 | ||||
1282 | g_data_input_stream_read_line_async (request->data, 0, NULL((void*)0), | |||
1283 | (GAsyncReadyCallback)got_http_request_line, request); | |||
1284 | return TRUE(!(0)); | |||
1285 | } | |||
1286 | ||||
1287 | BroadwayServer * | |||
1288 | broadway_server_new (char *address, | |||
1289 | int port, | |||
1290 | const char *ssl_cert, | |||
1291 | const char *ssl_key, | |||
1292 | GError **error) | |||
1293 | { | |||
1294 | BroadwayServer *server; | |||
1295 | ||||
1296 | server = g_object_new (BROADWAY_TYPE_SERVER(broadway_server_get_type()), NULL((void*)0)); | |||
1297 | server->port = port; | |||
1298 | server->address = g_strdup (address)g_strdup_inline (address); | |||
1299 | server->ssl_cert = g_strdup (ssl_cert)g_strdup_inline (ssl_cert); | |||
1300 | server->ssl_key = g_strdup (ssl_key)g_strdup_inline (ssl_key); | |||
1301 | ||||
1302 | if (address == NULL((void*)0)) | |||
1303 | { | |||
1304 | if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (server->service)((((GSocketListener*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server->service)), ((g_socket_listener_get_type ()))) ))), | |||
1305 | server->port, | |||
1306 | G_OBJECT (server)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server)), (((GType) ((20) << (2)))))))), | |||
1307 | error)) | |||
1308 | { | |||
1309 | g_prefix_error (error, "Unable to listen to port %d: ", server->port); | |||
1310 | g_object_unref (server); | |||
1311 | return NULL((void*)0); | |||
1312 | } | |||
1313 | } | |||
1314 | else | |||
1315 | { | |||
1316 | GInetAddress *inet_address; | |||
1317 | GSocketAddress *socket_address; | |||
1318 | ||||
1319 | inet_address = g_inet_address_new_from_string (address); | |||
1320 | if (inet_address == NULL((void*)0)) | |||
1321 | { | |||
1322 | g_set_error (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_INVALID_DATA, "Invalid ip address %s: ", address); | |||
1323 | g_object_unref (server); | |||
1324 | return NULL((void*)0); | |||
1325 | } | |||
1326 | socket_address = g_inet_socket_address_new (inet_address, port); | |||
1327 | g_object_unref (inet_address); | |||
1328 | if (!g_socket_listener_add_address (G_SOCKET_LISTENER (server->service)((((GSocketListener*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server->service)), ((g_socket_listener_get_type ()))) ))), | |||
1329 | socket_address, | |||
1330 | G_SOCKET_TYPE_STREAM, | |||
1331 | G_SOCKET_PROTOCOL_TCP, | |||
1332 | G_OBJECT (server)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server)), (((GType) ((20) << (2)))))))), | |||
1333 | NULL((void*)0), | |||
1334 | error)) | |||
1335 | { | |||
1336 | g_prefix_error (error, "Unable to listen to %s:%d: ", server->address, server->port); | |||
1337 | g_object_unref (socket_address); | |||
1338 | g_object_unref (server); | |||
1339 | return NULL((void*)0); | |||
1340 | } | |||
1341 | g_object_unref (socket_address); | |||
1342 | } | |||
1343 | ||||
1344 | g_signal_connect (server->service, "incoming",g_signal_connect_data ((server->service), ("incoming"), (( (GCallback) (handle_incoming_connection))), (((void*)0)), ((void *)0), (GConnectFlags) 0) | |||
1345 | G_CALLBACK (handle_incoming_connection), NULL)g_signal_connect_data ((server->service), ("incoming"), (( (GCallback) (handle_incoming_connection))), (((void*)0)), ((void *)0), (GConnectFlags) 0); | |||
1346 | return server; | |||
1347 | } | |||
1348 | ||||
1349 | BroadwayServer * | |||
1350 | broadway_server_on_unix_socket_new (char *address, GError **error) | |||
1351 | { | |||
1352 | BroadwayServer *server; | |||
1353 | GSocketAddress *socket_address = NULL((void*)0); | |||
1354 | ||||
1355 | server = g_object_new (BROADWAY_TYPE_SERVER(broadway_server_get_type()), NULL((void*)0)); | |||
1356 | server->port = -1; | |||
1357 | server->address = g_strdup (address)g_strdup_inline (address); | |||
1358 | ||||
1359 | if (address == NULL((void*)0)) | |||
1360 | { | |||
1361 | g_set_error (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_INVALID_DATA, "Unspecified unix domain socket address"); | |||
1362 | g_object_unref (server); | |||
1363 | return NULL((void*)0); | |||
1364 | } | |||
1365 | else | |||
1366 | { | |||
1367 | #ifdef HAVE_GIO_UNIX1 | |||
1368 | socket_address = g_unix_socket_address_new (address); | |||
1369 | #endif | |||
1370 | if (socket_address == NULL((void*)0)) | |||
1371 | { | |||
1372 | g_set_error (error, G_IO_ERRORg_io_error_quark(), G_IO_ERROR_INVALID_DATA, "Invalid unix domain socket address %s: ", address); | |||
1373 | g_object_unref (server); | |||
1374 | return NULL((void*)0); | |||
1375 | } | |||
1376 | if (!g_socket_listener_add_address (G_SOCKET_LISTENER (server->service)((((GSocketListener*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server->service)), ((g_socket_listener_get_type ()))) ))), | |||
1377 | socket_address, | |||
1378 | G_SOCKET_TYPE_STREAM, | |||
1379 | G_SOCKET_PROTOCOL_DEFAULT, | |||
1380 | G_OBJECT (server)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((server)), (((GType) ((20) << (2)))))))), | |||
1381 | NULL((void*)0), | |||
1382 | error)) | |||
1383 | { | |||
1384 | g_prefix_error (error, "Unable to listen to %s: ", server->address); | |||
1385 | g_object_unref (socket_address); | |||
1386 | g_object_unref (server); | |||
1387 | return NULL((void*)0); | |||
1388 | } | |||
1389 | g_object_unref (socket_address); | |||
1390 | } | |||
1391 | ||||
1392 | g_signal_connect (server->service, "incoming",g_signal_connect_data ((server->service), ("incoming"), (( (GCallback) (handle_incoming_connection))), (((void*)0)), ((void *)0), (GConnectFlags) 0) | |||
1393 | G_CALLBACK (handle_incoming_connection), NULL)g_signal_connect_data ((server->service), ("incoming"), (( (GCallback) (handle_incoming_connection))), (((void*)0)), ((void *)0), (GConnectFlags) 0); | |||
1394 | return server; | |||
1395 | } | |||
1396 | ||||
1397 | guint32 | |||
1398 | broadway_server_get_last_seen_time (BroadwayServer *server) | |||
1399 | { | |||
1400 | broadway_server_consume_all_input (server); | |||
1401 | return (guint32) server->last_seen_time; | |||
1402 | } | |||
1403 | ||||
1404 | void | |||
1405 | broadway_server_query_mouse (BroadwayServer *server, | |||
1406 | guint32 *toplevel, | |||
1407 | gint32 *root_x, | |||
1408 | gint32 *root_y, | |||
1409 | guint32 *mask) | |||
1410 | { | |||
1411 | if (server->output) | |||
1412 | { | |||
1413 | broadway_server_consume_all_input (server); | |||
1414 | if (root_x) | |||
1415 | *root_x = server->future_root_x; | |||
1416 | if (root_y) | |||
1417 | *root_y = server->future_root_y; | |||
1418 | if (mask) | |||
1419 | *mask = server->future_state; | |||
1420 | if (toplevel) | |||
1421 | *toplevel = server->future_mouse_in_toplevel; | |||
1422 | return; | |||
1423 | } | |||
1424 | ||||
1425 | /* Fallback when unconnected */ | |||
1426 | if (root_x) | |||
1427 | *root_x = server->last_x; | |||
1428 | if (root_y) | |||
1429 | *root_y = server->last_y; | |||
1430 | if (mask) | |||
1431 | *mask = server->last_state; | |||
1432 | if (toplevel) | |||
1433 | *toplevel = server->mouse_in_toplevel_id; | |||
1434 | } | |||
1435 | ||||
1436 | void | |||
1437 | broadway_server_destroy_window (BroadwayServer *server, | |||
1438 | gint id) | |||
1439 | { | |||
1440 | BroadwayWindow *window; | |||
1441 | ||||
1442 | if (server->mouse_in_toplevel_id == id) | |||
1443 | { | |||
1444 | /* TODO: Send leave + enter event, update cursors, etc */ | |||
1445 | server->mouse_in_toplevel_id = 0; | |||
1446 | } | |||
1447 | ||||
1448 | if (server->pointer_grab_window_id == id) | |||
1449 | server->pointer_grab_window_id = -1; | |||
1450 | ||||
1451 | if (server->output) | |||
1452 | broadway_output_destroy_surface (server->output, | |||
1453 | id); | |||
1454 | ||||
1455 | window = g_hash_table_lookup (server->id_ht, | |||
1456 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1457 | if (window != NULL((void*)0)) | |||
1458 | { | |||
1459 | server->toplevels = g_list_remove (server->toplevels, window); | |||
1460 | g_hash_table_remove (server->id_ht, | |||
1461 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1462 | ||||
1463 | g_free (window->cached_surface_name); | |||
1464 | if (window->cached_surface != NULL((void*)0)) | |||
1465 | cairo_surface_destroy (window->cached_surface); | |||
1466 | ||||
1467 | g_free (window); | |||
1468 | } | |||
1469 | } | |||
1470 | ||||
1471 | gboolean | |||
1472 | broadway_server_window_show (BroadwayServer *server, | |||
1473 | gint id) | |||
1474 | { | |||
1475 | BroadwayWindow *window; | |||
1476 | gboolean sent = FALSE(0); | |||
1477 | ||||
1478 | window = g_hash_table_lookup (server->id_ht, | |||
1479 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1480 | if (window == NULL((void*)0)) | |||
1481 | return FALSE(0); | |||
1482 | ||||
1483 | window->visible = TRUE(!(0)); | |||
1484 | ||||
1485 | if (server->output) | |||
1486 | { | |||
1487 | broadway_output_show_surface (server->output, window->id); | |||
1488 | sent = TRUE(!(0)); | |||
1489 | } | |||
1490 | ||||
1491 | return sent; | |||
1492 | } | |||
1493 | ||||
1494 | gboolean | |||
1495 | broadway_server_window_hide (BroadwayServer *server, | |||
1496 | gint id) | |||
1497 | { | |||
1498 | BroadwayWindow *window; | |||
1499 | gboolean sent = FALSE(0); | |||
1500 | ||||
1501 | window = g_hash_table_lookup (server->id_ht, | |||
1502 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1503 | if (window == NULL((void*)0)) | |||
1504 | return FALSE(0); | |||
1505 | ||||
1506 | window->visible = FALSE(0); | |||
1507 | ||||
1508 | if (server->mouse_in_toplevel_id == id) | |||
1509 | { | |||
1510 | /* TODO: Send leave + enter event, update cursors, etc */ | |||
1511 | server->mouse_in_toplevel_id = 0; | |||
1512 | } | |||
1513 | ||||
1514 | if (server->pointer_grab_window_id == id) | |||
1515 | server->pointer_grab_window_id = -1; | |||
1516 | ||||
1517 | if (server->output) | |||
1518 | { | |||
1519 | broadway_output_hide_surface (server->output, window->id); | |||
1520 | sent = TRUE(!(0)); | |||
1521 | } | |||
1522 | return sent; | |||
1523 | } | |||
1524 | ||||
1525 | void | |||
1526 | broadway_server_window_raise (BroadwayServer *server, | |||
1527 | gint id) | |||
1528 | { | |||
1529 | BroadwayWindow *window; | |||
1530 | ||||
1531 | window = g_hash_table_lookup (server->id_ht, | |||
1532 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1533 | if (window == NULL((void*)0)) | |||
1534 | return; | |||
1535 | ||||
1536 | server->toplevels = g_list_remove (server->toplevels, window); | |||
1537 | server->toplevels = g_list_append (server->toplevels, window); | |||
1538 | ||||
1539 | if (server->output) | |||
1540 | broadway_output_raise_surface (server->output, window->id); | |||
1541 | } | |||
1542 | ||||
1543 | void | |||
1544 | broadway_server_set_show_keyboard (BroadwayServer *server, | |||
1545 | gboolean show) | |||
1546 | { | |||
1547 | server->show_keyboard = show; | |||
1548 | ||||
1549 | if (server->output) | |||
1550 | { | |||
1551 | broadway_output_set_show_keyboard (server->output, server->show_keyboard); | |||
1552 | broadway_server_flush (server); | |||
1553 | } | |||
1554 | } | |||
1555 | ||||
1556 | void | |||
1557 | broadway_server_window_lower (BroadwayServer *server, | |||
1558 | gint id) | |||
1559 | { | |||
1560 | BroadwayWindow *window; | |||
1561 | ||||
1562 | window = g_hash_table_lookup (server->id_ht, | |||
1563 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1564 | if (window == NULL((void*)0)) | |||
1565 | return; | |||
1566 | ||||
1567 | server->toplevels = g_list_remove (server->toplevels, window); | |||
1568 | server->toplevels = g_list_prepend (server->toplevels, window); | |||
1569 | ||||
1570 | if (server->output) | |||
1571 | broadway_output_lower_surface (server->output, window->id); | |||
1572 | } | |||
1573 | ||||
1574 | void | |||
1575 | broadway_server_window_set_transient_for (BroadwayServer *server, | |||
1576 | gint id, gint parent) | |||
1577 | { | |||
1578 | BroadwayWindow *window; | |||
1579 | ||||
1580 | window = g_hash_table_lookup (server->id_ht, | |||
1581 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1582 | if (window == NULL((void*)0)) | |||
1583 | return; | |||
1584 | ||||
1585 | window->transient_for = parent; | |||
1586 | ||||
1587 | if (server->output) | |||
1588 | { | |||
1589 | broadway_output_set_transient_for (server->output, window->id, window->transient_for); | |||
1590 | broadway_server_flush (server); | |||
1591 | } | |||
1592 | } | |||
1593 | ||||
1594 | gboolean | |||
1595 | broadway_server_has_client (BroadwayServer *server) | |||
1596 | { | |||
1597 | return server->output != NULL((void*)0); | |||
1598 | } | |||
1599 | ||||
1600 | void | |||
1601 | broadway_server_window_update (BroadwayServer *server, | |||
1602 | gint id, | |||
1603 | cairo_surface_t *surface) | |||
1604 | { | |||
1605 | BroadwayWindow *window; | |||
1606 | BroadwayBuffer *buffer; | |||
1607 | ||||
1608 | if (surface == NULL((void*)0)) | |||
1609 | return; | |||
1610 | ||||
1611 | window = g_hash_table_lookup (server->id_ht, | |||
1612 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1613 | if (window == NULL((void*)0)) | |||
1614 | return; | |||
1615 | ||||
1616 | g_assert (window->width == cairo_image_surface_get_width (surface))do { if (window->width == cairo_image_surface_get_width (surface )) ; else g_assertion_message_expr ("Cdk", "broadway-server.c" , 1616, ((const char*) (__func__)), "window->width == cairo_image_surface_get_width (surface)" ); } while (0); | |||
1617 | g_assert (window->height == cairo_image_surface_get_height (surface))do { if (window->height == cairo_image_surface_get_height ( surface)) ; else g_assertion_message_expr ("Cdk", "broadway-server.c" , 1617, ((const char*) (__func__)), "window->height == cairo_image_surface_get_height (surface)" ); } while (0); | |||
1618 | ||||
1619 | buffer = broadway_buffer_create (window->width, window->height, | |||
1620 | cairo_image_surface_get_data (surface), | |||
1621 | cairo_image_surface_get_stride (surface)); | |||
1622 | ||||
1623 | if (server->output != NULL((void*)0)) | |||
1624 | { | |||
1625 | window->buffer_synced = TRUE(!(0)); | |||
1626 | broadway_output_put_buffer (server->output, window->id, | |||
1627 | window->buffer, buffer); | |||
1628 | } | |||
1629 | ||||
1630 | if (window->buffer) | |||
1631 | broadway_buffer_destroy (window->buffer); | |||
1632 | ||||
1633 | window->buffer = buffer; | |||
1634 | } | |||
1635 | ||||
1636 | gboolean | |||
1637 | broadway_server_window_move_resize (BroadwayServer *server, | |||
1638 | gint id, | |||
1639 | gboolean with_move, | |||
1640 | int x, | |||
1641 | int y, | |||
1642 | int width, | |||
1643 | int height) | |||
1644 | { | |||
1645 | BroadwayWindow *window; | |||
1646 | gboolean with_resize; | |||
1647 | gboolean sent = FALSE(0); | |||
1648 | ||||
1649 | window = g_hash_table_lookup (server->id_ht, | |||
1650 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1651 | if (window == NULL((void*)0)) | |||
1652 | return FALSE(0); | |||
1653 | ||||
1654 | with_resize = width != window->width || height != window->height; | |||
1655 | window->width = width; | |||
1656 | window->height = height; | |||
1657 | ||||
1658 | if (server->output != NULL((void*)0)) | |||
1659 | { | |||
1660 | broadway_output_move_resize_surface (server->output, | |||
1661 | window->id, | |||
1662 | with_move, x, y, | |||
1663 | with_resize, window->width, window->height); | |||
1664 | sent = TRUE(!(0)); | |||
1665 | } | |||
1666 | else | |||
1667 | { | |||
1668 | if (with_move) | |||
1669 | { | |||
1670 | window->x = x; | |||
1671 | window->y = y; | |||
1672 | } | |||
1673 | ||||
1674 | fake_configure_notify (server, window); | |||
1675 | } | |||
1676 | ||||
1677 | return sent; | |||
1678 | } | |||
1679 | ||||
1680 | void | |||
1681 | broadway_server_focus_window (BroadwayServer *server, | |||
1682 | gint new_focused_window) | |||
1683 | { | |||
1684 | BroadwayInputMsg focus_msg; | |||
1685 | ||||
1686 | if (server->focused_window_id == new_focused_window) | |||
1687 | return; | |||
1688 | ||||
1689 | memset (&focus_msg, 0, sizeof (focus_msg)); | |||
1690 | focus_msg.base.type = BROADWAY_EVENT_FOCUS; | |||
1691 | focus_msg.base.time = broadway_server_get_last_seen_time (server); | |||
1692 | focus_msg.focus.old_id = server->focused_window_id; | |||
1693 | focus_msg.focus.new_id = new_focused_window; | |||
1694 | ||||
1695 | broadway_events_got_input (&focus_msg, -1); | |||
1696 | ||||
1697 | /* Keep track of the new focused window */ | |||
1698 | server->focused_window_id = new_focused_window; | |||
1699 | } | |||
1700 | ||||
1701 | guint32 | |||
1702 | broadway_server_grab_pointer (BroadwayServer *server, | |||
1703 | gint client_id, | |||
1704 | gint id, | |||
1705 | gboolean owner_events, | |||
1706 | guint32 event_mask G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1707 | guint32 time_) | |||
1708 | { | |||
1709 | if (server->pointer_grab_window_id != -1 && | |||
1710 | time_ != 0 && server->pointer_grab_time > time_) | |||
1711 | return CDK_GRAB_ALREADY_GRABBED; | |||
1712 | ||||
1713 | if (time_ == 0) | |||
1714 | time_ = server->last_seen_time; | |||
1715 | ||||
1716 | server->pointer_grab_window_id = id; | |||
1717 | server->pointer_grab_client_id = client_id; | |||
1718 | server->pointer_grab_owner_events = owner_events; | |||
1719 | server->pointer_grab_time = time_; | |||
1720 | ||||
1721 | if (server->output) | |||
1722 | { | |||
1723 | broadway_output_grab_pointer (server->output, | |||
1724 | id, | |||
1725 | owner_events); | |||
1726 | broadway_server_flush (server); | |||
1727 | } | |||
1728 | ||||
1729 | /* TODO: What about toplevel grab events if we're not connected? */ | |||
1730 | ||||
1731 | return CDK_GRAB_SUCCESS; | |||
1732 | } | |||
1733 | ||||
1734 | guint32 | |||
1735 | broadway_server_ungrab_pointer (BroadwayServer *server, | |||
1736 | guint32 time_) | |||
1737 | { | |||
1738 | guint32 serial; | |||
1739 | ||||
1740 | if (server->pointer_grab_window_id != -1 && | |||
1741 | time_ != 0 && server->pointer_grab_time > time_) | |||
1742 | return 0; | |||
1743 | ||||
1744 | /* TODO: What about toplevel grab events if we're not connected? */ | |||
1745 | ||||
1746 | if (server->output) | |||
1747 | { | |||
1748 | serial = broadway_output_ungrab_pointer (server->output); | |||
1749 | broadway_server_flush (server); | |||
1750 | } | |||
1751 | else | |||
1752 | { | |||
1753 | serial = server->saved_serial; | |||
1754 | } | |||
1755 | ||||
1756 | server->pointer_grab_window_id = -1; | |||
1757 | ||||
1758 | return serial; | |||
1759 | } | |||
1760 | ||||
1761 | static const cairo_user_data_key_t shm_cairo_key; | |||
1762 | ||||
1763 | typedef struct { | |||
1764 | void *data; | |||
1765 | gsize data_size; | |||
1766 | } ShmSurfaceData; | |||
1767 | ||||
1768 | static void | |||
1769 | shm_data_unmap (void *_data) | |||
1770 | { | |||
1771 | ShmSurfaceData *data = _data; | |||
1772 | #ifdef G_OS_UNIX | |||
1773 | munmap (data->data, data->data_size); | |||
1774 | #elif defined(G_OS_WIN32) | |||
1775 | UnmapViewOfFile (data->data); | |||
1776 | #endif | |||
1777 | g_free (data); | |||
1778 | } | |||
1779 | ||||
1780 | cairo_surface_t * | |||
1781 | broadway_server_open_surface (BroadwayServer *server, | |||
1782 | guint32 id, | |||
1783 | char *name, | |||
1784 | int width, | |||
1785 | int height) | |||
1786 | { | |||
1787 | BroadwayWindow *window; | |||
1788 | ShmSurfaceData *data; | |||
1789 | cairo_surface_t *surface; | |||
1790 | gsize size; | |||
1791 | void *ptr; | |||
1792 | ||||
1793 | window = g_hash_table_lookup (server->id_ht, | |||
1794 | GINT_TO_POINTER (id)((gpointer) (glong) (id))); | |||
1795 | if (window == NULL((void*)0)) | |||
1796 | return NULL((void*)0); | |||
1797 | ||||
1798 | if (window->cached_surface_name != NULL((void*)0) && | |||
1799 | strcmp (name, window->cached_surface_name) == 0) | |||
1800 | return cairo_surface_reference (window->cached_surface); | |||
1801 | ||||
1802 | size = width * height * sizeof (guint32); | |||
1803 | ||||
1804 | ptr = map_named_shm (name, size); | |||
1805 | ||||
1806 | if (ptr == NULL((void*)0)) | |||
1807 | return NULL((void*)0); | |||
1808 | ||||
1809 | data = g_new0 (ShmSurfaceData, 1)((ShmSurfaceData *) g_malloc0_n ((1), sizeof (ShmSurfaceData) )); | |||
1810 | ||||
1811 | data->data = ptr; | |||
1812 | data->data_size = size; | |||
1813 | ||||
1814 | surface = cairo_image_surface_create_for_data ((guchar *)data->data, | |||
1815 | CAIRO_FORMAT_ARGB32, | |||
1816 | width, height, | |||
1817 | width * sizeof (guint32)); | |||
1818 | g_assert (surface != NULL)do { if (surface != ((void*)0)) ; else g_assertion_message_expr ("Cdk", "broadway-server.c", 1818, ((const char*) (__func__) ), "surface != NULL"); } while (0); | |||
1819 | ||||
1820 | cairo_surface_set_user_data (surface, &shm_cairo_key, | |||
1821 | data, shm_data_unmap); | |||
1822 | ||||
1823 | g_free (window->cached_surface_name); | |||
1824 | window->cached_surface_name = g_strdup (name)g_strdup_inline (name); | |||
1825 | ||||
1826 | if (window->cached_surface != NULL((void*)0)) | |||
1827 | cairo_surface_destroy (window->cached_surface); | |||
1828 | window->cached_surface = cairo_surface_reference (surface); | |||
1829 | ||||
1830 | return surface; | |||
1831 | } | |||
1832 | ||||
1833 | guint32 | |||
1834 | broadway_server_new_window (BroadwayServer *server, | |||
1835 | int x, | |||
1836 | int y, | |||
1837 | int width, | |||
1838 | int height, | |||
1839 | gboolean is_temp) | |||
1840 | { | |||
1841 | BroadwayWindow *window; | |||
1842 | ||||
1843 | window = g_new0 (BroadwayWindow, 1)((BroadwayWindow *) g_malloc0_n ((1), sizeof (BroadwayWindow) )); | |||
1844 | window->id = server->id_counter++; | |||
1845 | window->x = x; | |||
1846 | window->y = y; | |||
1847 | if (x == 0 && y == 0 && !is_temp) | |||
1848 | { | |||
1849 | /* TODO: Better way to know if we should pick default pos */ | |||
1850 | window->x = 100; | |||
1851 | window->y = 100; | |||
1852 | } | |||
1853 | window->width = width; | |||
1854 | window->height = height; | |||
1855 | window->is_temp = is_temp; | |||
1856 | ||||
1857 | g_hash_table_insert (server->id_ht, | |||
1858 | GINT_TO_POINTER (window->id)((gpointer) (glong) (window->id)), | |||
1859 | window); | |||
1860 | ||||
1861 | server->toplevels = g_list_append (server->toplevels, window); | |||
1862 | ||||
1863 | if (server->output) | |||
1864 | broadway_output_new_surface (server->output, | |||
1865 | window->id, | |||
1866 | window->x, | |||
1867 | window->y, | |||
1868 | window->width, | |||
1869 | window->height, | |||
1870 | window->is_temp); | |||
1871 | else | |||
1872 | fake_configure_notify (server, window); | |||
1873 | ||||
1874 | return window->id; | |||
1875 | } | |||
1876 | ||||
1877 | static void | |||
1878 | broadway_server_resync_windows (BroadwayServer *server) | |||
1879 | { | |||
1880 | GList *l; | |||
1881 | ||||
1882 | if (server->output == NULL((void*)0)) | |||
1883 | return; | |||
1884 | ||||
1885 | /* First create all windows */ | |||
1886 | for (l = server->toplevels; l != NULL((void*)0); l = l->next) | |||
1887 | { | |||
1888 | BroadwayWindow *window = l->data; | |||
1889 | ||||
1890 | if (window->id == 0) | |||
1891 | continue; /* Skip root */ | |||
1892 | ||||
1893 | window->buffer_synced = FALSE(0); | |||
1894 | broadway_output_new_surface (server->output, | |||
1895 | window->id, | |||
1896 | window->x, | |||
1897 | window->y, | |||
1898 | window->width, | |||
1899 | window->height, | |||
1900 | window->is_temp); | |||
1901 | } | |||
1902 | ||||
1903 | /* Then do everything that may reference other windows */ | |||
1904 | for (l = server->toplevels; l != NULL((void*)0); l = l->next) | |||
1905 | { | |||
1906 | BroadwayWindow *window = l->data; | |||
1907 | ||||
1908 | if (window->id == 0) | |||
1909 | continue; /* Skip root */ | |||
1910 | ||||
1911 | if (window->transient_for != -1) | |||
1912 | broadway_output_set_transient_for (server->output, window->id, window->transient_for); | |||
1913 | if (window->visible) | |||
1914 | { | |||
1915 | broadway_output_show_surface (server->output, window->id); | |||
1916 | ||||
1917 | if (window->buffer != NULL((void*)0)) | |||
1918 | { | |||
1919 | window->buffer_synced = TRUE(!(0)); | |||
1920 | broadway_output_put_buffer (server->output, window->id, | |||
1921 | NULL((void*)0), window->buffer); | |||
1922 | } | |||
1923 | } | |||
1924 | } | |||
1925 | ||||
1926 | if (server->show_keyboard) | |||
1927 | broadway_output_set_show_keyboard (server->output, TRUE(!(0))); | |||
1928 | ||||
1929 | broadway_server_flush (server); | |||
1930 | } |