 6af85a62bb
			
		
	
	6af85a62bb
	
	
	
		
			
			returning focus to transient for window on destroyng window with focus; raising modal window and moving focus into it Solves problem with focus loss in complex multi window UI.
		
			
				
	
	
		
			852 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			852 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "config.h"
 | |
| 
 | |
| #ifdef HAVE_SYS_MMAN_H
 | |
| #include <sys/mman.h>
 | |
| #endif
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| 
 | |
| #include "gdkbroadway-server.h"
 | |
| 
 | |
| #include "gdkprivate-broadway.h"
 | |
| 
 | |
| #include <glib.h>
 | |
| #include <glib/gprintf.h>
 | |
| #ifdef G_OS_UNIX
 | |
| #include <gio/gunixsocketaddress.h>
 | |
| #endif
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #ifdef HAVE_UNISTD_H
 | |
| #include <unistd.h>
 | |
| #elif defined (G_OS_WIN32)
 | |
| #include <io.h>
 | |
| #define ftruncate _chsize_s
 | |
| #endif
 | |
| #include <sys/types.h>
 | |
| #ifdef G_OS_WIN32
 | |
| #include <windows.h>
 | |
| #endif
 | |
| #include "gdkintl.h"
 | |
| 
 | |
| typedef struct BroadwayInput BroadwayInput;
 | |
| 
 | |
| struct _GdkBroadwayServer {
 | |
|   GObject parent_instance;
 | |
| 
 | |
|   guint32 next_serial;
 | |
|   GSocketConnection *connection;
 | |
| 
 | |
|   guint32 recv_buffer_size;
 | |
|   guint8 recv_buffer[1024];
 | |
| 
 | |
|   guint process_input_idle;
 | |
|   GList *incomming;
 | |
|   
 | |
| };
 | |
| 
 | |
| struct _GdkBroadwayServerClass
 | |
| {
 | |
|   GObjectClass parent_class;
 | |
| };
 | |
| 
 | |
| static gboolean input_available_cb (gpointer stream, gpointer user_data);
 | |
| 
 | |
| static GType gdk_broadway_server_get_type (void);
 | |
| 
 | |
| G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT)
 | |
| 
 | |
| static void
 | |
| gdk_broadway_server_init (GdkBroadwayServer *server)
 | |
| {
 | |
|   server->next_serial = 1;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gdk_broadway_server_lookahead_event (GdkBroadwayServer  *server,
 | |
| 				      const char         *types)
 | |
| {
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| gulong
 | |
| _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server)
 | |
| {
 | |
|   return (gulong)server->next_serial;
 | |
| }
 | |
| 
 | |
| GdkBroadwayServer *
 | |
| _gdk_broadway_server_new (const char *display, GError **error)
 | |
| {
 | |
|   GdkBroadwayServer *server;
 | |
|   GSocketClient *client;
 | |
|   GSocketConnection *connection;
 | |
|   GInetAddress *inet;
 | |
|   GSocketAddress *address;
 | |
|   GPollableInputStream *pollable;
 | |
|   GInputStream *in;
 | |
|   GSource *source;
 | |
|   char *local_socket_type = NULL;
 | |
|   int port;
 | |
| 
 | |
|   if (display == NULL)
 | |
|     {
 | |
| #ifdef G_OS_UNIX
 | |
|       if (g_unix_socket_address_abstract_names_supported ())
 | |
|         display = ":0";
 | |
|       else
 | |
| #endif
 | |
|         display = ":tcp";
 | |
|     }
 | |
| 
 | |
|   if (g_str_has_prefix (display, ":tcp"))
 | |
|     {
 | |
|       port = 9090 + strtol (display + strlen (":tcp"), NULL, 10);
 | |
| 
 | |
|       inet = g_inet_address_new_from_string ("127.0.0.1");
 | |
|       address = g_inet_socket_address_new (inet, port);
 | |
|       g_object_unref (inet);
 | |
|     }
 | |
| #ifdef G_OS_UNIX
 | |
|   else if (display[0] == ':' && g_ascii_isdigit(display[1]))
 | |
|     {
 | |
|       char *path, *basename;
 | |
| 
 | |
|       port = strtol (display + strlen (":"), NULL, 10);
 | |
|       basename = g_strdup_printf ("broadway%d.socket", port + 1);
 | |
|       path = g_build_filename (g_get_user_runtime_dir (), basename, NULL);
 | |
|       g_free (basename);
 | |
| 
 | |
|       address = g_unix_socket_address_new_with_type (path, -1,
 | |
| 						     G_UNIX_SOCKET_ADDRESS_ABSTRACT);
 | |
|       g_free (path);
 | |
|     }
 | |
| #endif
 | |
|   else
 | |
|     {
 | |
|       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
 | |
| 		   _("Broadway display type not supported: %s"), display);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   g_free (local_socket_type);
 | |
| 
 | |
|   client = g_socket_client_new ();
 | |
| 
 | |
|   connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (address), NULL, error);
 | |
| 
 | |
|   g_object_unref (address);
 | |
|   g_object_unref (client);
 | |
| 
 | |
|   if (connection == NULL)
 | |
|     return NULL;
 | |
| 
 | |
|   server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL);
 | |
|   server->connection = connection;
 | |
| 
 | |
|   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
 | |
|   pollable = G_POLLABLE_INPUT_STREAM (in);
 | |
| 
 | |
|   source = g_pollable_input_stream_create_source (pollable, NULL);
 | |
|   g_source_attach (source, NULL);
 | |
|   g_source_set_callback (source, (GSourceFunc)input_available_cb, server, NULL);
 | |
| 
 | |
|   return server;
 | |
| }
 | |
| 
 | |
| guint32
 | |
| _gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server)
 | |
| {
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static guint32
 | |
| gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayRequestBase *base,
 | |
| 					    gsize size, guint32 type)
 | |
| {
 | |
|   GOutputStream *out;
 | |
|   gsize written;
 | |
| 
 | |
|   base->size = size;
 | |
|   base->type = type;
 | |
|   base->serial = server->next_serial++;
 | |
| 
 | |
|   out = g_io_stream_get_output_stream (G_IO_STREAM (server->connection));
 | |
| 
 | |
|   if (!g_output_stream_write_all (out, base, size, &written, NULL, NULL))
 | |
|     {
 | |
|       g_printerr ("Unable to write to server\n");
 | |
|       exit (1);
 | |
|     }
 | |
| 
 | |
|   g_assert (written == size);
 | |
| 
 | |
|   return base->serial;
 | |
| }
 | |
| 
 | |
| #define gdk_broadway_server_send_message(_server, _msg, _type) \
 | |
|   gdk_broadway_server_send_message_with_size(_server, (BroadwayRequestBase *)&_msg, sizeof (_msg), _type)
 | |
| 
 | |
| static void
 | |
| parse_all_input (GdkBroadwayServer *server)
 | |
| {
 | |
|   guint8 *p, *end;
 | |
|   guint32 size;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   p = server->recv_buffer;
 | |
|   end = p + server->recv_buffer_size;
 | |
| 
 | |
|   while (p + sizeof (guint32) <= end)
 | |
|     {
 | |
|       memcpy (&size, p, sizeof (guint32));
 | |
|       if (p + size > end)
 | |
| 	break;
 | |
| 
 | |
|       reply = g_memdup (p, size);
 | |
|       p += size;
 | |
| 
 | |
|       server->incomming = g_list_append (server->incomming, reply);
 | |
|     }
 | |
| 
 | |
|   if (p < end)
 | |
|     memmove (server->recv_buffer, p, end - p);
 | |
|   server->recv_buffer_size = end - p;
 | |
| }
 | |
| 
 | |
| static void
 | |
| read_some_input_blocking (GdkBroadwayServer *server)
 | |
| {
 | |
|   GInputStream *in;
 | |
|   gssize res;
 | |
| 
 | |
|   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
 | |
| 
 | |
|   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
 | |
|   res = g_input_stream_read (in, &server->recv_buffer[server->recv_buffer_size],
 | |
| 			     sizeof (server->recv_buffer) - server->recv_buffer_size,
 | |
| 			     NULL, NULL);
 | |
| 
 | |
|   if (res <= 0)
 | |
|     {
 | |
|       g_printerr ("Unable to read from broadway server\n");
 | |
|       exit (1);
 | |
|     }
 | |
| 
 | |
|   server->recv_buffer_size += res;
 | |
| }
 | |
| 
 | |
| static void
 | |
| read_some_input_nonblocking (GdkBroadwayServer *server)
 | |
| {
 | |
|   GInputStream *in;
 | |
|   GPollableInputStream *pollable;
 | |
|   gssize res;
 | |
|   GError *error;
 | |
| 
 | |
|   in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection));
 | |
|   pollable = G_POLLABLE_INPUT_STREAM (in);
 | |
| 
 | |
|   g_assert (server->recv_buffer_size < sizeof (server->recv_buffer));
 | |
|   error = NULL;
 | |
|   res = g_pollable_input_stream_read_nonblocking (pollable, &server->recv_buffer[server->recv_buffer_size],
 | |
| 						  sizeof (server->recv_buffer) - server->recv_buffer_size,
 | |
| 						  NULL, &error);
 | |
| 
 | |
|   if (res < 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
 | |
|     {
 | |
|       g_error_free (error);
 | |
|       res = 0;
 | |
|     }
 | |
|   else if (res <= 0)
 | |
|     {
 | |
|       g_printerr ("Unable to read from broadway server: %s\n", error ? error->message : "eof");
 | |
|       exit (1);
 | |
|     }
 | |
| 
 | |
|   server->recv_buffer_size += res;
 | |
| }
 | |
| 
 | |
| static BroadwayReply *
 | |
| find_response_by_serial (GdkBroadwayServer *server, guint32 serial)
 | |
| {
 | |
|   GList *l;
 | |
| 
 | |
|   for (l = server->incomming; l != NULL; l = l->next)
 | |
|     {
 | |
|       BroadwayReply *reply = l->data;
 | |
| 
 | |
|       if (reply->base.in_reply_to == serial)
 | |
| 	return reply;
 | |
|     }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| process_input_messages (GdkBroadwayServer *server)
 | |
| {
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   if (server->process_input_idle != 0)
 | |
|     {
 | |
|       g_source_remove (server->process_input_idle);
 | |
|       server->process_input_idle = 0;
 | |
|     }
 | |
| 
 | |
|   while (server->incomming)
 | |
|     {
 | |
|       reply = server->incomming->data;
 | |
|       server->incomming =
 | |
| 	g_list_delete_link (server->incomming,
 | |
| 			    server->incomming);
 | |
| 
 | |
|       if (reply->base.type == BROADWAY_REPLY_EVENT)
 | |
| 	_gdk_broadway_events_got_input (&reply->event.msg);
 | |
|       else
 | |
| 	g_warning ("Unhandled reply type %d", reply->base.type);
 | |
|       g_free (reply);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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 gboolean
 | |
| input_available_cb (gpointer stream, gpointer user_data)
 | |
| {
 | |
|   GdkBroadwayServer *server = user_data;
 | |
| 
 | |
|   read_some_input_nonblocking (server);
 | |
|   parse_all_input (server);
 | |
| 
 | |
|   process_input_messages (server);
 | |
| 
 | |
|   return G_SOURCE_CONTINUE;
 | |
| }
 | |
| 
 | |
| static BroadwayReply *
 | |
| gdk_broadway_server_wait_for_reply (GdkBroadwayServer *server,
 | |
| 				    guint32 serial)
 | |
| {
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   while (TRUE)
 | |
|     {
 | |
|       reply = find_response_by_serial (server, serial);
 | |
|       if (reply)
 | |
| 	{
 | |
| 	  server->incomming = g_list_remove (server->incomming, reply);
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       read_some_input_blocking (server);
 | |
|       parse_all_input (server);
 | |
|     }
 | |
| 
 | |
|   queue_process_input_at_idle (server);
 | |
|   return reply;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_flush (GdkBroadwayServer *server)
 | |
| {
 | |
|   BroadwayRequestFlush msg;
 | |
| 
 | |
|   gdk_broadway_server_send_message(server, msg, BROADWAY_REQUEST_FLUSH);
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_sync (GdkBroadwayServer *server)
 | |
| {
 | |
|   BroadwayRequestSync msg;
 | |
|   guint32 serial;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   serial = gdk_broadway_server_send_message (server, msg,
 | |
| 					     BROADWAY_REQUEST_SYNC);
 | |
|   reply = gdk_broadway_server_wait_for_reply (server, serial);
 | |
| 
 | |
|   g_assert (reply->base.type == BROADWAY_REPLY_SYNC);
 | |
| 
 | |
|   g_free (reply);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_query_mouse (GdkBroadwayServer *server,
 | |
| 				  guint32            *toplevel,
 | |
| 				  gint32             *root_x,
 | |
| 				  gint32             *root_y,
 | |
| 				  guint32            *mask)
 | |
| {
 | |
|   BroadwayRequestQueryMouse msg;
 | |
|   guint32 serial;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   serial = gdk_broadway_server_send_message (server, msg,
 | |
| 					     BROADWAY_REQUEST_QUERY_MOUSE);
 | |
|   reply = gdk_broadway_server_wait_for_reply (server, serial);
 | |
| 
 | |
|   g_assert (reply->base.type == BROADWAY_REPLY_QUERY_MOUSE);
 | |
| 
 | |
|   if (toplevel)
 | |
|     *toplevel = reply->query_mouse.toplevel;
 | |
|   if (root_x)
 | |
|     *root_x = reply->query_mouse.root_x;
 | |
|   if (root_y)
 | |
|     *root_y = reply->query_mouse.root_y;
 | |
|   if (mask)
 | |
|     *mask = reply->query_mouse.mask;
 | |
|   
 | |
|   g_free (reply);
 | |
| }
 | |
| 
 | |
| guint32
 | |
| _gdk_broadway_server_new_window (GdkBroadwayServer *server,
 | |
| 				 int x,
 | |
| 				 int y,
 | |
| 				 int width,
 | |
| 				 int height,
 | |
| 				 gboolean is_temp)
 | |
| {
 | |
|   BroadwayRequestNewWindow msg;
 | |
|   guint32 serial, id;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   msg.x = x;
 | |
|   msg.y = y;
 | |
|   msg.width = width;
 | |
|   msg.height = height;
 | |
|   msg.is_temp = is_temp;
 | |
| 
 | |
|   serial = gdk_broadway_server_send_message (server, msg,
 | |
| 					     BROADWAY_REQUEST_NEW_WINDOW);
 | |
|   reply = gdk_broadway_server_wait_for_reply (server, serial);
 | |
| 
 | |
|   g_assert (reply->base.type == BROADWAY_REPLY_NEW_WINDOW);
 | |
| 
 | |
|   id = reply->new_window.id;
 | |
|   
 | |
|   g_free (reply);
 | |
| 
 | |
|   return id;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_destroy_window (GdkBroadwayServer *server,
 | |
| 				     gint id)
 | |
| {
 | |
|   BroadwayRequestDestroyWindow msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_DESTROY_WINDOW);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gdk_broadway_server_window_show (GdkBroadwayServer *server,
 | |
| 				  gint id)
 | |
| {
 | |
|   BroadwayRequestShowWindow msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_SHOW_WINDOW);
 | |
|   
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gdk_broadway_server_window_hide (GdkBroadwayServer *server,
 | |
| 				  gint id)
 | |
| {
 | |
|   BroadwayRequestHideWindow msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_HIDE_WINDOW);
 | |
|   
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_window_focus (GdkBroadwayServer *server,
 | |
| 				   gint id)
 | |
| {
 | |
|   BroadwayRequestFocusWindow msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_FOCUS_WINDOW);
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
 | |
| 					       gint id, gint parent)
 | |
| {
 | |
|   BroadwayRequestSetTransientFor msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   msg.parent = parent;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_SET_TRANSIENT_FOR);
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_window_set_modal_hint (GdkBroadwayServer *server,
 | |
| 					    gint id, gboolean modal_hint)
 | |
| {
 | |
|   BroadwayRequestSetModalHint msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   msg.modal_hint = modal_hint;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_SET_MODAL_HINT);
 | |
| }
 | |
| 
 | |
| static void *
 | |
| map_named_shm (char *name, gsize size, gboolean *is_shm)
 | |
| {
 | |
| #ifdef G_OS_UNIX
 | |
| 
 | |
|   char *filename = NULL;
 | |
|   int fd;
 | |
|   void *ptr;
 | |
|   int res;
 | |
| 
 | |
|   fd = shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 | |
|   if (fd == -1)
 | |
|     {
 | |
|       if (errno == EEXIST)
 | |
| 	return NULL;
 | |
| 
 | |
|       filename = g_build_filename (g_get_tmp_dir (), name, NULL);
 | |
| 
 | |
|       fd = open (filename, O_RDWR | O_CREAT | O_EXCL, 0600);
 | |
|       g_free (filename);
 | |
|       if (fd == -1)
 | |
| 	{
 | |
| 	  if (errno != EEXIST)
 | |
| 	    g_error ("Unable to allocate shared mem for window");
 | |
| 	  return NULL;
 | |
| 	}
 | |
|       else
 | |
| 	*is_shm = FALSE;
 | |
|     }
 | |
|   else
 | |
|     *is_shm = TRUE;
 | |
| 
 | |
|   res = ftruncate (fd, size);
 | |
|   g_assert (res != -1);
 | |
| 
 | |
| #ifdef HAVE_POSIX_FALLOCATE
 | |
|   res = posix_fallocate (fd, 0, size);
 | |
|   if (res != 0 && errno == ENOSPC)
 | |
|     {
 | |
|       if (filename)
 | |
| 	unlink (filename);
 | |
|       else
 | |
| 	shm_unlink (name);
 | |
|       g_error ("Not enough shared memory for window surface");
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 | |
| 
 | |
|   (void) close(fd);
 | |
| 
 | |
|   return ptr;
 | |
| 
 | |
| #elif defined(G_OS_WIN32)
 | |
| 
 | |
|   int fd;
 | |
|   void *ptr;
 | |
|   char *shmpath;
 | |
|   void *map = ((void *)-1);
 | |
|   int res;
 | |
| 
 | |
|   if (*name == '/')
 | |
|     ++name;
 | |
|   shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);
 | |
| 
 | |
|   fd = open(shmpath, O_RDWR|O_CREAT|O_EXCL, 0600);
 | |
|   g_free (shmpath);
 | |
|   if (fd == -1)
 | |
|     {
 | |
|       if (errno != EEXIST)
 | |
| 	g_error ("Unable to allocate shared mem for window");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   *is_shm = TRUE;
 | |
|   res = ftruncate (fd, size);
 | |
|   g_assert (res != -1);
 | |
|   
 | |
|   if (size == 0)
 | |
|     ptr = map;
 | |
|   else
 | |
|     {
 | |
|       HANDLE h, fm;
 | |
|       h = (HANDLE)_get_osfhandle (fd);
 | |
|       fm = CreateFileMapping (h, NULL, PAGE_READWRITE, 0, (DWORD)size, NULL);
 | |
|       ptr = MapViewOfFile (fm, FILE_MAP_WRITE, 0, 0, (size_t)size);
 | |
|       CloseHandle (fm);
 | |
|     }
 | |
| 
 | |
|   (void) close(fd);
 | |
| 
 | |
|   return ptr;
 | |
| 
 | |
| #else
 | |
| #error "No shm mapping supported"
 | |
| 
 | |
|   return NULL;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static char
 | |
| make_valid_fs_char (char c)
 | |
| {
 | |
|   char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
 | |
| 
 | |
|   return chars[c % (sizeof (chars) - 1)];
 | |
| }
 | |
| 
 | |
| /* name must have at least space for 34 bytes */
 | |
| static gpointer
 | |
| create_random_shm (char *name, gsize size, gboolean *is_shm)
 | |
| {
 | |
|   guint32 r;
 | |
|   int i, o;
 | |
|   gpointer ptr;
 | |
| 
 | |
|   while (TRUE)
 | |
|     {
 | |
|       o = 0;
 | |
|       name[o++] = '/';
 | |
|       name[o++] = 'b';
 | |
|       name[o++] = 'd';
 | |
|       name[o++] = 'w';
 | |
|       name[o++] = '-';
 | |
|       for (i = 0; i < 32/4 - 1; i++)
 | |
| 	{
 | |
| 	  r = g_random_int ();
 | |
| 	  name[o++] = make_valid_fs_char ((r >> 0) & 0xff);
 | |
| 	  name[o++] = make_valid_fs_char ((r >> 8) & 0xff);
 | |
| 	  name[o++] = make_valid_fs_char ((r >> 16) & 0xff);
 | |
| 	  name[o++] = make_valid_fs_char ((r >> 24) & 0xff);
 | |
| 	}
 | |
|       name[o++] = 0;
 | |
| 
 | |
|       ptr = map_named_shm (name, size, is_shm);
 | |
|       if (ptr)
 | |
| 	return ptr;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const cairo_user_data_key_t gdk_broadway_shm_cairo_key;
 | |
| 
 | |
| typedef struct {
 | |
|   char name[36];
 | |
|   void *data;
 | |
|   gsize data_size;
 | |
|   gboolean is_shm;
 | |
| } BroadwayShmSurfaceData;
 | |
| 
 | |
| static void
 | |
| shm_data_destroy (void *_data)
 | |
| {
 | |
|   BroadwayShmSurfaceData *data = _data;
 | |
| 
 | |
| #ifdef G_OS_UNIX
 | |
| 
 | |
|   munmap (data->data, data->data_size);
 | |
|   if (data->is_shm)
 | |
|     shm_unlink (data->name);
 | |
|   else
 | |
|     {
 | |
|       char *filename = g_build_filename (g_get_tmp_dir (), data->name, NULL);
 | |
|       unlink (filename);
 | |
|       g_free (filename);
 | |
|     }
 | |
| 
 | |
| #elif defined(G_OS_WIN32)
 | |
| 
 | |
|   char *name = data->name;
 | |
|   char *shmpath;
 | |
| 
 | |
|   if (*name == '/')
 | |
|     ++name;
 | |
| 
 | |
|   shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);
 | |
|   UnmapViewOfFile (data->data);
 | |
|   remove (shmpath);
 | |
|   g_free (shmpath);
 | |
| 
 | |
| #endif
 | |
| 
 | |
|   g_free (data);
 | |
| }
 | |
| 
 | |
| cairo_surface_t *
 | |
| _gdk_broadway_server_create_surface (int                 width,
 | |
| 				     int                 height)
 | |
| {
 | |
|   BroadwayShmSurfaceData *data;
 | |
|   cairo_surface_t *surface;
 | |
| 
 | |
|   data = g_new (BroadwayShmSurfaceData, 1);
 | |
|   data->data_size = width * height * sizeof (guint32);
 | |
|   data->data = create_random_shm (data->name, data->data_size, &data->is_shm);
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data ((guchar *)data->data,
 | |
| 						 CAIRO_FORMAT_ARGB32, width, height, width * sizeof (guint32));
 | |
|   g_assert (surface != NULL);
 | |
|   
 | |
|   cairo_surface_set_user_data (surface, &gdk_broadway_shm_cairo_key,
 | |
| 			       data, shm_data_destroy);
 | |
| 
 | |
|   return surface;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_window_update (GdkBroadwayServer *server,
 | |
| 				    gint id,
 | |
| 				    cairo_surface_t *surface)
 | |
| {
 | |
|   BroadwayRequestUpdate msg;
 | |
|   BroadwayShmSurfaceData *data;
 | |
| 
 | |
|   if (surface == NULL)
 | |
|     return;
 | |
| 
 | |
|   data = cairo_surface_get_user_data (surface, &gdk_broadway_shm_cairo_key);
 | |
|   g_assert (data != NULL);
 | |
| 
 | |
|   msg.id = id;
 | |
|   memcpy (msg.name, data->name, 36);
 | |
|   msg.width = cairo_image_surface_get_width (surface);
 | |
|   msg.height = cairo_image_surface_get_height (surface);
 | |
| 
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_UPDATE);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
 | |
| 					 gint id,
 | |
| 					 gboolean with_move,
 | |
| 					 int x,
 | |
| 					 int y,
 | |
| 					 int width,
 | |
| 					 int height)
 | |
| {
 | |
|   BroadwayRequestMoveResize msg;
 | |
| 
 | |
|   msg.id = id;
 | |
|   msg.with_move = with_move;
 | |
|   msg.x = x;
 | |
|   msg.y = y;
 | |
|   msg.width = width;
 | |
|   msg.height = height;
 | |
| 
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_MOVE_RESIZE);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| GdkGrabStatus
 | |
| _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
 | |
| 				   gint id,
 | |
| 				   gboolean owner_events,
 | |
| 				   guint32 event_mask,
 | |
| 				   guint32 time_)
 | |
| {
 | |
|   BroadwayRequestGrabPointer msg;
 | |
|   guint32 serial, status;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   msg.id = id;
 | |
|   msg.owner_events = owner_events;
 | |
|   msg.event_mask = event_mask;
 | |
|   msg.time_ = time_;
 | |
| 
 | |
|   serial = gdk_broadway_server_send_message (server, msg,
 | |
| 					     BROADWAY_REQUEST_GRAB_POINTER);
 | |
|   reply = gdk_broadway_server_wait_for_reply (server, serial);
 | |
| 
 | |
|   g_assert (reply->base.type == BROADWAY_REPLY_GRAB_POINTER);
 | |
| 
 | |
|   status = reply->grab_pointer.status;
 | |
| 
 | |
|   g_free (reply);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| guint32
 | |
| _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
 | |
| 				     guint32    time_)
 | |
| {
 | |
|   BroadwayRequestUngrabPointer msg;
 | |
|   guint32 serial, status;
 | |
|   BroadwayReply *reply;
 | |
| 
 | |
|   msg.time_ = time_;
 | |
| 
 | |
|   serial = gdk_broadway_server_send_message (server, msg,
 | |
| 					     BROADWAY_REQUEST_UNGRAB_POINTER);
 | |
|   reply = gdk_broadway_server_wait_for_reply (server, serial);
 | |
| 
 | |
|   g_assert (reply->base.type == BROADWAY_REPLY_UNGRAB_POINTER);
 | |
| 
 | |
|   status = reply->ungrab_pointer.status;
 | |
| 
 | |
|   g_free (reply);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gdk_broadway_server_set_show_keyboard (GdkBroadwayServer *server,
 | |
|                                         gboolean show)
 | |
| {
 | |
|   BroadwayRequestSetShowKeyboard msg;
 | |
| 
 | |
|   msg.show_keyboard = show;
 | |
|   gdk_broadway_server_send_message (server, msg,
 | |
| 				    BROADWAY_REQUEST_SET_SHOW_KEYBOARD);
 | |
| }
 |