mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 19:00:08 +00:00
75fd7f09af
2001-06-25 Alexander Larsson <alexl@redhat.com> * configure.in: Added --enable-fbmanager. This is some experimental code that lets several GtkFB apps coordinate their access to the framebuffer. * acconfig.h: Added ENABLE_FB_MANAGER. * gdk/linux-fb/Makefile.am: Added gdkfbmanager and gdkfbswitch. * gdk/linux-fb/gdkkeyboard-fb.c: * gdk/linux-fb/gdkmouse-fb.c: * gdk/linux-fb/gdkprivate-fb.h: Split device init and open so that they can be opened and closed while switched away. * gdk/linux-fb/gdkmain-fb.c: Add the basic manager communication. * gdk/linux-fb/gdkrender-fb.c: Don't update to the shadow fb if we're blocked by the fb manager.
407 lines
7.9 KiB
C
407 lines
7.9 KiB
C
#include <glib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
#include "gdkfbmanager.h"
|
|
|
|
typedef struct {
|
|
int socket;
|
|
int pid; /* -1 if not initialized */
|
|
} Client;
|
|
|
|
GHashTable *clients = NULL;
|
|
GHashTable *new_clients = NULL;
|
|
Client *current_owner = NULL;
|
|
|
|
int master_socket;
|
|
|
|
int create_master_socket (void)
|
|
{
|
|
int fd;
|
|
struct sockaddr_un addr;
|
|
|
|
fd = socket (PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (fd < 0)
|
|
{
|
|
printf ("Error creating socket: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
unlink ("/tmp/.fb.manager");
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy (addr.sun_path, "/tmp/.fb.manager");
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) < 0)
|
|
{
|
|
printf ("Unable to bind socket: %s\n", strerror(errno));
|
|
close (fd);
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (listen (fd, 10) < 0)
|
|
{
|
|
printf ("Unable to listen on socket: %s\n", strerror(errno));
|
|
close (fd);
|
|
return -1;
|
|
}
|
|
|
|
master_socket = fd;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
handle_new_client (void)
|
|
{
|
|
int fd;
|
|
Client *client;
|
|
int true_val;
|
|
|
|
fd = accept (master_socket, NULL, NULL);
|
|
|
|
client = g_new (Client, 1);
|
|
client->socket = fd;
|
|
client->pid = -1;
|
|
|
|
true_val = 1;
|
|
setsockopt (fd, SOL_SOCKET, SO_PASSCRED,
|
|
&true_val, sizeof (true_val));
|
|
|
|
g_print ("Handling new client %p conntecting, fd = %d\n", client, fd);
|
|
|
|
g_hash_table_insert (new_clients, client, client);
|
|
}
|
|
|
|
struct fd_data
|
|
{
|
|
fd_set *read_fds;
|
|
fd_set *exception_fds;
|
|
int max_fd;
|
|
};
|
|
|
|
void
|
|
send_message (Client *client, enum FBManagerMessageType type, int data)
|
|
{
|
|
struct FBManagerMessage msg;
|
|
|
|
msg.msg_type = type;
|
|
msg.data = data;
|
|
|
|
send (client->socket, &msg, sizeof (msg), 0);
|
|
}
|
|
|
|
gboolean
|
|
wait_for_ack (Client *client, int timeout_secs)
|
|
{
|
|
struct FBManagerMessage msg;
|
|
int res;
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
|
|
while (1)
|
|
{
|
|
FD_ZERO(&rfds);
|
|
FD_SET(client->socket, &rfds);
|
|
|
|
tv.tv_sec = timeout_secs;
|
|
tv.tv_usec = 0;
|
|
|
|
res = select (client->socket+1, &rfds, NULL, NULL, &tv);
|
|
|
|
if (res == 0)
|
|
return FALSE;
|
|
|
|
res = recv (client->socket, &msg, sizeof (msg), 0);
|
|
if (res != sizeof (msg))
|
|
return FALSE;
|
|
|
|
if (msg.msg_type == FB_MANAGER_ACK)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
find_another_client (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
Client **res;
|
|
Client *client;
|
|
|
|
res = user_data;
|
|
|
|
if (*res)
|
|
return;
|
|
|
|
client = value;
|
|
if (client != current_owner)
|
|
*res = client;
|
|
}
|
|
|
|
void
|
|
switch_to_client (Client *client)
|
|
{
|
|
g_print ("Switch_to_client, client=%p, current_owner=%p\n", client, current_owner);
|
|
|
|
if ((current_owner == client) && (client != NULL))
|
|
return;
|
|
|
|
if (current_owner)
|
|
{
|
|
g_print ("switching from client fd=%d\n", current_owner->socket);
|
|
send_message (current_owner, FB_MANAGER_SWITCH_FROM, 0);
|
|
wait_for_ack (current_owner, 3);
|
|
}
|
|
|
|
current_owner = client;
|
|
|
|
if (current_owner)
|
|
{
|
|
g_print ("switching to client fd=%d\n", current_owner->socket);
|
|
send_message (current_owner, FB_MANAGER_SWITCH_TO, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
close_client (Client *client)
|
|
{
|
|
Client *other_client;
|
|
g_print ("Closing client %p (fd=%d)\n",
|
|
client, client->socket);
|
|
|
|
if (current_owner == client)
|
|
{
|
|
other_client = NULL;
|
|
g_hash_table_foreach (clients,
|
|
find_another_client,
|
|
&other_client);
|
|
current_owner = NULL;
|
|
/* FIXME: This is a hack around the fact that the serial
|
|
mouse driver had problems with opening and closing
|
|
the device almost at the same time.
|
|
*/
|
|
sleep (1);
|
|
switch_to_client (other_client);
|
|
}
|
|
|
|
close (client->socket);
|
|
g_free (client);
|
|
}
|
|
|
|
|
|
/* Returns TRUE if the client was closed */
|
|
gboolean
|
|
read_client_data (Client *client)
|
|
{
|
|
struct FBManagerMessage fb_message;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
char control_buffer[256];
|
|
struct cmsghdr *cmsg;
|
|
int res;
|
|
struct ucred *creds;
|
|
Client *new_client;
|
|
|
|
iov.iov_base = &fb_message;
|
|
iov.iov_len = sizeof (fb_message);
|
|
|
|
cmsg = (struct cmsghdr *)control_buffer;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsg;
|
|
msg.msg_controllen = 256;
|
|
msg.msg_flags = 0;
|
|
|
|
g_print ("Reading client data:");
|
|
res = recvmsg (client->socket, &msg, 0);
|
|
g_print ("%d bytes, (error: %s)\n", res,
|
|
strerror (errno));
|
|
|
|
if (res < 0)
|
|
return FALSE;
|
|
|
|
if (res == 0)
|
|
{
|
|
close_client (client);
|
|
return TRUE;
|
|
}
|
|
|
|
if (res != sizeof (fb_message))
|
|
{
|
|
g_warning ("Packet with wrong size %d recieved", res);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (fb_message.msg_type) {
|
|
case FB_MANAGER_NEW_CLIENT:
|
|
if (client->pid != -1)
|
|
{
|
|
g_warning ("Got a NEW_CLIENT message from an old client");
|
|
return FALSE;
|
|
}
|
|
creds = NULL;
|
|
for (cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg != NULL;
|
|
cmsg = CMSG_NXTHDR(&msg,cmsg))
|
|
{
|
|
if (cmsg->cmsg_level == SOL_SOCKET &&
|
|
cmsg->cmsg_type == SCM_CREDENTIALS)
|
|
{
|
|
creds = (struct ucred *) CMSG_DATA(cmsg);
|
|
break;
|
|
}
|
|
}
|
|
if (creds == NULL)
|
|
{
|
|
g_warning ("Got no credentials in NEW_CLIENT message");
|
|
close_client (client);
|
|
return TRUE;
|
|
}
|
|
client->pid = creds->pid;
|
|
|
|
g_hash_table_insert (clients, GINT_TO_POINTER (client->pid), client);
|
|
|
|
g_print ("New client connected. Pid=%d\n", (int)creds->pid);
|
|
return TRUE;
|
|
break;
|
|
case FB_MANAGER_REQUEST_SWITCH_TO_PID:
|
|
if (client->pid == -1)
|
|
{
|
|
g_warning ("Got a message from an uninitialized client");
|
|
return FALSE;
|
|
}
|
|
|
|
new_client = g_hash_table_lookup (clients, GINT_TO_POINTER (fb_message.data));
|
|
if (new_client)
|
|
switch_to_client (new_client);
|
|
else
|
|
g_warning ("Switchto unknown PID");
|
|
break;
|
|
case FB_MANAGER_ACK:
|
|
if (client->pid == -1)
|
|
{
|
|
g_warning ("Got a message from an uninitialized client");
|
|
return FALSE;
|
|
}
|
|
g_warning ("Got an unexpected ACK");
|
|
break;
|
|
default:
|
|
g_warning ("Got unknown package type %d", fb_message.msg_type);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Returns TRUE if the client was closed */
|
|
gboolean
|
|
handle_client_data (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
Client *client;
|
|
struct fd_data *data;
|
|
|
|
client = value;
|
|
data = user_data;
|
|
|
|
if (FD_ISSET (client->socket, data->exception_fds))
|
|
{
|
|
close_client (client);
|
|
return TRUE;
|
|
}
|
|
else if (FD_ISSET (client->socket, data->read_fds))
|
|
{
|
|
return read_client_data (client);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
set_fds (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
struct fd_data *data;
|
|
Client *client;
|
|
|
|
client = value;
|
|
data = user_data;
|
|
|
|
FD_SET (client->socket, data->read_fds);
|
|
FD_SET (client->socket, data->exception_fds);
|
|
data->max_fd = MAX (data->max_fd,
|
|
client->socket);
|
|
}
|
|
|
|
void
|
|
main_loop (void)
|
|
{
|
|
fd_set read_fds;
|
|
fd_set exception_fds;
|
|
struct fd_data data;
|
|
int res;
|
|
|
|
while (1)
|
|
{
|
|
FD_ZERO (&read_fds);
|
|
FD_ZERO (&exception_fds);
|
|
FD_SET (master_socket, &read_fds);
|
|
|
|
data.read_fds = &read_fds;
|
|
data.exception_fds = &exception_fds;
|
|
data.max_fd = master_socket;
|
|
|
|
g_hash_table_foreach (clients,
|
|
set_fds,
|
|
&data);
|
|
g_hash_table_foreach (new_clients,
|
|
set_fds,
|
|
&data);
|
|
|
|
|
|
res = select (data.max_fd+1,
|
|
&read_fds, NULL, &exception_fds,
|
|
NULL);
|
|
|
|
if (FD_ISSET (master_socket, &read_fds))
|
|
handle_new_client ();
|
|
|
|
g_hash_table_foreach_remove (clients,
|
|
handle_client_data,
|
|
&data);
|
|
g_hash_table_foreach_remove (new_clients,
|
|
handle_client_data,
|
|
&data);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
clients = g_hash_table_new (g_direct_hash,
|
|
g_direct_equal);
|
|
new_clients = g_hash_table_new (g_direct_hash,
|
|
g_direct_equal);
|
|
|
|
create_master_socket ();
|
|
|
|
main_loop ();
|
|
|
|
return 0;
|
|
}
|