From 31f26c2915caa6320af74ba73ebf71491ab80fa3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 21 Oct 2007 17:54:45 +0200 Subject: Basic autoconfization --- Makefile.am | 75 +++ bootstrap.sh | 69 +++ configure.ac | 141 +++++ lassi-avahi.c | 255 -------- lassi-avahi.h | 31 - lassi-clipboard.c | 174 ------ lassi-clipboard.h | 24 - lassi-grab.c | 407 ------------- lassi-grab.h | 40 -- lassi-order.c | 147 ----- lassi-order.h | 13 - lassi-osd.c | 144 ----- lassi-osd.h | 18 - lassi-prefs.c | 212 ------- lassi-prefs.h | 29 - lassi-server.c | 1554 ------------------------------------------------- lassi-server.h | 84 --- lassi-tray.c | 100 ---- lassi-tray.h | 32 - mango-lassi.glade | 300 ---------- src/lassi-avahi.c | 255 ++++++++ src/lassi-avahi.h | 31 + src/lassi-clipboard.c | 174 ++++++ src/lassi-clipboard.h | 24 + src/lassi-grab.c | 407 +++++++++++++ src/lassi-grab.h | 40 ++ src/lassi-order.c | 147 +++++ src/lassi-order.h | 13 + src/lassi-osd.c | 144 +++++ src/lassi-osd.h | 18 + src/lassi-prefs.c | 212 +++++++ src/lassi-prefs.h | 29 + src/lassi-server.c | 1554 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lassi-server.h | 84 +++ src/lassi-tray.c | 100 ++++ src/lassi-tray.h | 32 + src/mango-lassi.glade | 300 ++++++++++ src/x | 16 + src/xinput.c | 102 ++++ x | 16 - xinput.c | 102 ---- 41 files changed, 3967 insertions(+), 3682 deletions(-) create mode 100644 Makefile.am create mode 100755 bootstrap.sh create mode 100644 configure.ac delete mode 100644 lassi-avahi.c delete mode 100644 lassi-avahi.h delete mode 100644 lassi-clipboard.c delete mode 100644 lassi-clipboard.h delete mode 100644 lassi-grab.c delete mode 100644 lassi-grab.h delete mode 100644 lassi-order.c delete mode 100644 lassi-order.h delete mode 100644 lassi-osd.c delete mode 100644 lassi-osd.h delete mode 100644 lassi-prefs.c delete mode 100644 lassi-prefs.h delete mode 100644 lassi-server.c delete mode 100644 lassi-server.h delete mode 100644 lassi-tray.c delete mode 100644 lassi-tray.h delete mode 100644 mango-lassi.glade create mode 100644 src/lassi-avahi.c create mode 100644 src/lassi-avahi.h create mode 100644 src/lassi-clipboard.c create mode 100644 src/lassi-clipboard.h create mode 100644 src/lassi-grab.c create mode 100644 src/lassi-grab.h create mode 100644 src/lassi-order.c create mode 100644 src/lassi-order.h create mode 100644 src/lassi-osd.c create mode 100644 src/lassi-osd.h create mode 100644 src/lassi-prefs.c create mode 100644 src/lassi-prefs.h create mode 100644 src/lassi-server.c create mode 100644 src/lassi-server.h create mode 100644 src/lassi-tray.c create mode 100644 src/lassi-tray.h create mode 100644 src/mango-lassi.glade create mode 100755 src/x create mode 100644 src/xinput.c delete mode 100755 x delete mode 100644 xinput.c diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c151499 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,75 @@ +# This file is part of Mango Lassi. +# +# Copyright 207 Lennart Poettering +# +# Mango Lassi is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Mango Lassi is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Mango Lassi; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +gladedir = $(pkgdatadir) +localedir = $(datadir)/locale + +SUBDIRS = po + +bin_PROGRAMS = \ + mango-lassi + +mango_lassi_SOURCES = \ + src/lassi-server.c src/lassi-server.h \ + src/lassi-grab.c src/lassi-grab.h \ + src/lassi-osd.c src/lassi-osd.h \ + src/lassi-order.c src/lassi-order.h \ + src/lassi-clipboard.c src/lassi-clipboard.h \ + src/lassi-avahi.c src/lassi-avahi.h \ + src/lassi-tray.c src/lassi-avahi.h \ + src/lassi-prefs.c src/lassi-prefs.h + +mango_lassi_LDADD = \ + $(AM_LDADD) \ + $(AVAHI_LIBS) \ + $(DBUS_LIBS) \ + $(GTK_LIBS) \ + $(XTEST_LIBS) \ + $(AVAHI_LIBS) \ + $(AVAHI_UI_LIBS) \ + $(LIBNOTIFY_LIBS) \ + $(LIBGLADE_LIBS) + +mango_lassi_CFLAGS = \ + $(AM_CFLAGS) \ + $(AVAHI_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GTK_CFLAGS) \ + $(XTEST_CFLAGS) \ + $(AVAHI_CFLAGS) \ + $(AVAHI_UI_CFLAGS) \ + $(LIBNOTIFY_CFLAGS) \ + $(LIBGLADE_CFLAGS) \ + -DGLADE_FILE=\"$(gladedir)/mango-lassi.glade\" + +glade_DATA = \ + src/mango-lassi.glade + +EXTRA_DIST = \ + intltool-merge.in \ + intltool-update.in \ + intltool-extract.in \ + src/mango-lassi.glade + +DISTCLEANFILES = \ + intltool-extract \ + intltool-merge \ + intltool-update + +ACLOCAL_AMFLAGS = -I m4 diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..089d445 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# This file is part of Mango Lassi. +# +# Copyright 2007 Lennart Poettering +# +# Mango Lassi is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Mango Lassi is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Mango Lassi; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +VERSION=1.9 + +run_versioned() { + local P + local V + + V=$(echo "$2" | sed -e 's,\.,,g') + + if [ -e "`which $1$V 2> /dev/null`" ] ; then + P="$1$V" + else + if [ -e "`which $1-$2 2> /dev/null`" ] ; then + P="$1-$2" + else + P="$1" + fi + fi + + shift 2 + "$P" "$@" +} + +set -ex + +if [ "x$1" = "xam" ] ; then + run_versioned automake "$VERSION" -a -c --foreign + ./config.status +else + rm -rf autom4te.cache + rm -f config.cache + + touch config.rpath + + rm -f Makefile.am~ configure.ac~ + echo "no" | gettextize --copy --force + test -f Makefile.am~ && mv Makefile.am~ Makefile.am + test -f configure.ac~ && mv configure.ac~ configure.ac + + intltoolize --copy --force --automake + run_versioned aclocal "$VERSION" -I m4 + run_versioned autoconf 2.59 -Wall + run_versioned autoheader 2.59 + run_versioned automake "$VERSION" --copy --foreign --add-missing + + if test "x$NOCONFIGURE" = "x"; then + CFLAGS="-g -O0" ./configure --sysconfdir=/etc --localstatedir=/var "$@" + make clean + fi +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..324170f --- /dev/null +++ b/configure.ac @@ -0,0 +1,141 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# This file is part of Mango Lassi. +# +# Copyright 2007 Lennart Poettering +# +# Mango Lassi is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Mango Lassi is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Mango Lassi; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +AC_PREREQ(2.57) + +AC_INIT([mango-lassi], 000,[mzcbylcnhqvb (at) 0pointer (dot) net]) +AC_CONFIG_SRCDIR([src/lassi-server.c]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([foreign -Wall]) + +AC_SUBST(PACKAGE_URL, [http://git.0pointer.de/?p=mango-lassi.git]) + +if type -p stow > /dev/null && test -d /usr/local/stow ; then + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +fi + +# CC + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_GCC_TRADITIONAL +AC_GNU_SOURCE + +# GCC flags + +test_gcc_flag() { + AC_LANG_CONFTEST([int main(int argc, char*argv[]) {}]) + $CC -c conftest.c $CFLAGS -o conftest.o > /dev/null 2> /dev/null + ret=$? + rm -f conftest.o + return $ret +} + +# If using GCC specify some additional parameters +if test "x$GCC" = "xyes" ; then + + # We use gnu99 instead of c99 because many have interpreted the standard + # in a way that int64_t isn't defined on non-64 bit platforms. + DESIRED_FLAGS="-std=gnu99 -Wall -W -Wextra -pedantic -pipe -Wformat -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wno-unused-parameter -ffast-math" + + for flag in $DESIRED_FLAGS ; do + AC_MSG_CHECKING([whether $CC accepts $flag]) + if test_gcc_flag $flag ; then + CFLAGS="$CFLAGS $flag" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + done +fi + +#### Checks for header files. #### + +# ISO +AC_HEADER_STDC + +#### Typdefs, structures, etc. #### + +AC_C_CONST + +#### Large File-Support (LFS) #### + +AC_SYS_LARGEFILE + +#### pkg-config #### + +# Check for pkg-config manually first, as if its not installed the +# PKG_PROG_PKG_CONFIG macro won't be defined. +AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no) + +if test x"$have_pkg_config" = "xno"; then + AC_MSG_ERROR(pkg-config is required to install this program) +fi + +PKG_PROG_PKG_CONFIG + +#### D-Bus #### + +PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.1.1 dbus-glib-1 ]) +AC_SUBST(DBUS_CFLAGS) +AC_SUBST(DBUS_LIBS) + +PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ]) +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) + +PKG_CHECK_MODULES(XTEST, [ xtst ]) +AC_SUBST(XTEST_CFLAGS) +AC_SUBST(XTEST_LIBS) + +PKG_CHECK_MODULES(AVAHI, [ avahi-glib avahi-client ]) +AC_SUBST(AVAHI_CFLAGS) +AC_SUBST(AVAHI_LIBS) + +PKG_CHECK_MODULES(AVAHI_UI, [ avahi-ui ]) +AC_SUBST(AVAHI_UI_CFLAGS) +AC_SUBST(AVAHI_UI_LIBS) + +PKG_CHECK_MODULES(LIBNOTIFY, [ libnotify ]) +AC_SUBST(LIBNOTIFY_CFLAGS) +AC_SUBST(LIBNOTIFY_LIBS) + +PKG_CHECK_MODULES(LIBGLADE, [ libglade-2.0 ]) +AC_SUBST(LIBGLADE_CFLAGS) +AC_SUBST(LIBGLADE_LIBS) + + +AM_GNU_GETTEXT([external]) + +IT_PROG_INTLTOOL([0.35.0]) +GETTEXT_PACKAGE=mango-lassi +AC_SUBST([GETTEXT_PACKAGE]) +AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[Gettext package]) +AM_GLIB_GNU_GETTEXT + + +AC_CONFIG_FILES([ +Makefile +po/Makefile.in +]) + +AC_OUTPUT diff --git a/lassi-avahi.c b/lassi-avahi.c deleted file mode 100644 index 258c199..0000000 --- a/lassi-avahi.c +++ /dev/null @@ -1,255 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "lassi-avahi.h" - -/* FIXME: Error and collision handling is suboptimal */ - -static void resolve_cb( - AvahiServiceResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const char *name, - const char *type, - const char *domain, - const char *host_name, - const AvahiAddress *address, - uint16_t port, - AvahiStringList *txt, - AvahiLookupResultFlags flags, - void* userdata) { - - LassiAvahiInfo *i = userdata; - - g_assert(r); - g_assert(i); - - /* Called whenever a service has been resolved successfully or timed out */ - - switch (event) { - case AVAHI_RESOLVER_FOUND: { - char a[AVAHI_ADDRESS_STR_MAX], *t; - - avahi_address_snprint(a, sizeof(a), address); - t = g_strdup_printf("tcp:port=%u,host=%s", port, a); - lassi_server_connect(i->server, t); - g_free(t); - break; - } - - case AVAHI_RESOLVER_FAILURE: - g_message("Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror(avahi_client_errno(i->client))); - break; - - } - - avahi_service_resolver_free(r); - } - -static void browse_cb( - AvahiServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *type, - const char *domain, - AvahiLookupResultFlags flags, - void* userdata) { - - LassiAvahiInfo *i = userdata; - - g_assert(b); - g_assert(i); - - switch (event) { - case AVAHI_BROWSER_NEW: - - if (!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN) && - !lassi_server_is_connected(i->server, name) && - lassi_server_is_known(i->server, name)) - - if (!(avahi_service_resolver_new(i->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_cb, i))) - g_message("Failed to resolve service '%s': %s", name, avahi_strerror(avahi_client_errno(i->client))); - break; - - case AVAHI_BROWSER_REMOVE: - case AVAHI_BROWSER_ALL_FOR_NOW: - case AVAHI_BROWSER_CACHE_EXHAUSTED: - break; - - case AVAHI_BROWSER_FAILURE: - g_message("Browsing failed: %s", avahi_strerror(avahi_client_errno(i->client))); - gtk_main_quit(); - break; - } -} - - -static void create_service(LassiAvahiInfo *i); - -static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { - LassiAvahiInfo *i = userdata; - - g_assert(g); - g_assert(i); - - i->group = g; - - switch (state) { - case AVAHI_ENTRY_GROUP_ESTABLISHED : - g_message("Service '%s' successfully established.", i->service_name); - break; - - case AVAHI_ENTRY_GROUP_COLLISION : { - char *n; - - n = avahi_alternative_service_name(i->service_name); - avahi_free(i->service_name); - i->service_name = n; - - g_message("Service name collision, renaming service to '%s'", n); - - /* And recreate the services */ - create_service(i); - break; - } - - case AVAHI_ENTRY_GROUP_FAILURE : - g_message("Entry group failure: %s", avahi_strerror(avahi_client_errno(i->client))); - gtk_main_quit(); - break; - - case AVAHI_ENTRY_GROUP_UNCOMMITED: - case AVAHI_ENTRY_GROUP_REGISTERING: - ; - } -} - -static void create_service(LassiAvahiInfo *i) { - g_assert(i); - - if (!i->group) - if (!(i->group = avahi_entry_group_new(i->client, entry_group_callback, i))) { - g_message("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(i->client))); - gtk_main_quit(); - return; - } - - if (avahi_entry_group_is_empty(i->group)) { - int ret; - - for (;;) { - - if (!i->service_name) - i->service_name = g_strdup(i->server->id); - - if ((ret = avahi_entry_group_add_service(i->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, i->service_name, LASSI_SERVICE_TYPE, NULL, NULL, i->server->port, NULL)) < 0) { - - if (ret == AVAHI_ERR_COLLISION) { - char *n = avahi_alternative_service_name(i->service_name); - avahi_free(i->service_name); - i->service_name = n; - continue; - } - - g_message("Failed to add service: %s", avahi_strerror(ret)); - gtk_main_quit(); - return; - } - - break; - } - - if (strcmp(i->service_name, i->server->id)) { - g_free(i->server->id); - i->server->id = g_strdup(i->service_name); - } - - if ((ret = avahi_entry_group_commit(i->group)) < 0) { - g_message("Failed to commit entry group: %s", avahi_strerror(ret)); - gtk_main_quit(); - return; - } - } -} - -static void client_cb(AvahiClient *client, AvahiClientState state, void *userdata) { - LassiAvahiInfo *i = userdata; - - i->client = client; - - switch (state) { - case AVAHI_CLIENT_S_RUNNING: - if (!i->group) - create_service(i); - break; - - case AVAHI_CLIENT_FAILURE: - g_message("Client failure: %s", avahi_strerror(avahi_client_errno(client))); - gtk_main_quit(); - break; - - case AVAHI_CLIENT_S_COLLISION: - case AVAHI_CLIENT_S_REGISTERING: - if (i->group) - avahi_entry_group_reset(i->group); - - break; - - case AVAHI_CLIENT_CONNECTING: - ; - } -} - -int lassi_avahi_init(LassiAvahiInfo *i, LassiServer *server) { - int error; - - g_assert(i); - g_assert(server); - - memset(i, 0, sizeof(*i)); - i->server = server; - - avahi_set_allocator(avahi_glib_allocator()); - - if (!(i->poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) { - g_message("avahi_glib_poll_new() failed."); - goto fail; - } - - if (!(i->client = avahi_client_new(avahi_glib_poll_get(i->poll), 0, client_cb, i, &error))) { - g_message("avahi_client_new() failed: %s", avahi_strerror(error)); - goto fail; - } - - if (!(i->browser = avahi_service_browser_new(i->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LASSI_SERVICE_TYPE, NULL, 0, browse_cb, i))) { - g_message("avahi_service_browser_new(): %s", avahi_strerror(avahi_client_errno(i->client))); - goto fail; - } - - return 0; - -fail: - lassi_avahi_done(i); - return -1; -} - -void lassi_avahi_done(LassiAvahiInfo *i) { - g_assert(i); - - if (i->client) - avahi_client_free(i->client); - - if (i->poll) - avahi_glib_poll_free(i->poll); - - g_free(i->service_name); - - memset(i, 0, sizeof(*i)); -} diff --git a/lassi-avahi.h b/lassi-avahi.h deleted file mode 100644 index 04b634c..0000000 --- a/lassi-avahi.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef foolassiavahihfoo -#define foolassiavahihfoo - -#include -#include -#include -#include - -typedef struct LassiAvahiInfo LassiAvahiInfo; -struct LassiServer; - -struct LassiAvahiInfo { - struct LassiServer *server; - - AvahiGLibPoll *poll; - AvahiClient *client; - - AvahiEntryGroup *group; - char *service_name; - - AvahiServiceBrowser *browser; -}; - -#include "lassi-server.h" - -int lassi_avahi_init(LassiAvahiInfo *i, LassiServer *server); -void lassi_avahi_done(LassiAvahiInfo *i); - -#define LASSI_SERVICE_TYPE "_mango-lassi._tcp" - -#endif diff --git a/lassi-clipboard.c b/lassi-clipboard.c deleted file mode 100644 index bdfeff4..0000000 --- a/lassi-clipboard.c +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include - -#include "lassi-server.h" - -#define LASSI_MARKER "application/x-mango-lassi-marker" - -static void targets_received(GtkClipboard *clipboard, GdkAtom *atoms, int n_atoms, gpointer userdata) { - int j, k; - LassiClipboardInfo *i = userdata; - char **targets; - - g_assert(clipboard); - g_assert(i); - - g_debug("recvd targs %p, %i", atoms, n_atoms); - - if (!atoms) - return; - - targets = g_new0(char*, n_atoms+1); - - for (j = 0, k = 0; j < n_atoms; j++) { - char *c = gdk_atom_name(atoms[j]); - - /* Avoid loops */ - if (strcmp(c, LASSI_MARKER) == 0) { - g_free(c); - goto fail; - } - - if (strcmp(c, "TIMESTAMP") == 0 || - strcmp(c, "TARGETS") == 0 || - strcmp(c, "CLIPBOARD_MANAGER") == 0 || - strcmp(c, "CLIENT_WINDOW") == 0 || - strcmp(c, "DELETE") == 0 || - strcmp(c, "INSERT_PROPERTY") == 0 || - strcmp(c, "INSERT_SELECTION") == 0 || - strcmp(c, "LENGTH") == 0 || - strcmp(c, "TASK") == 0 || - strcmp(c, "MULTIPLE") == 0 || - strcmp(c, "DRAWABLE") == 0) { - g_free(c); - continue; - } - - targets[k++] = c; - } - - g_debug("%p %i", targets, n_atoms); - lassi_server_acquire_clipboard(i->server, clipboard == i->primary, targets); - -fail: - g_strfreev(targets); -} - -static void owner_change(GtkClipboard *clipboard, GdkEventOwnerChange *event, gpointer userdata) { - LassiClipboardInfo *i = userdata; - - g_assert(clipboard); - g_assert(i); - - g_debug("owner change"); - - if (event->reason == GDK_OWNER_CHANGE_NEW_OWNER) - gtk_clipboard_request_targets(clipboard, targets_received, i); - else - lassi_server_return_clipboard(i->server, clipboard == i->primary); -} - -static void get_func(GtkClipboard *clipboard, GtkSelectionData *sd, guint info, gpointer userdata) { - LassiClipboardInfo *i = userdata; - char *t; - int f = 0; - gpointer d = NULL; - gint l = 0; - - g_assert(clipboard); - g_assert(i); - - t = gdk_atom_name(sd->target); - - g_debug("get(%s)", t); - - if (lassi_server_get_clipboard(i->server, clipboard == i->primary, t, &f, &d, &l) >= 0) { - g_debug("successfully got data"); - gtk_selection_data_set(sd, sd->target, f, d, l); - } else - g_debug("failed to get data"); - - g_free(d); - g_free(t); -} - -static void clear_func(GtkClipboard *clipboard, gpointer userdata) { - LassiClipboardInfo *i = userdata; - - g_assert(clipboard); - g_assert(i); - - g_debug("clear"); - - gtk_clipboard_request_targets(clipboard, targets_received, i); -} - -int lassi_clipboard_init(LassiClipboardInfo *i, LassiServer *s) { - g_assert(i); - g_assert(s); - - memset(i, 0, sizeof(*i)); - i->server = s; - - i->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - i->primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY); - - g_signal_connect(i->clipboard, "owner_change", G_CALLBACK(owner_change), i); - g_signal_connect(i->primary, "owner_change", G_CALLBACK(owner_change), i); - return 0; -} - -void lassi_clipboard_done(LassiClipboardInfo *i) { - g_assert(i); - - memset(i, 0, sizeof(*i)); -} - -void lassi_clipboard_set(LassiClipboardInfo *i, gboolean primary, char *targets[]) { - int n = 0, j; - gboolean b; - char **t; - GtkTargetEntry *e; - - for (t = targets; *t; t++) - n++; - - e = g_new0(GtkTargetEntry, n+1); - - for (t = targets, j = 0; *t; t++, j++) { - e[j].target = *t; - e[j].info = j; - } - - e[j].target = LASSI_MARKER; - e[j].info = j; - - g_debug("setting %i targets", n+1); - - b = gtk_clipboard_set_with_data(primary ? i->primary : i->clipboard, e, n+1, get_func, clear_func, i); - g_assert(b); -} - -void lassi_clipboard_clear(LassiClipboardInfo *i, gboolean primary) { - g_assert(i); - - gtk_clipboard_clear(primary ? i->primary : i->clipboard); -} - -int lassi_clipboard_get(LassiClipboardInfo *i, gboolean primary, const char *target, int *f, gpointer *p, int *l) { - GtkSelectionData*sd; - g_assert(i); - - if (!(sd = gtk_clipboard_wait_for_contents(primary ? i->primary : i->clipboard, gdk_atom_intern(target, TRUE)))) - return -1; - - g_assert(sd->length > 0); - - *f = sd->format; - *p = g_memdup(sd->data, sd->length); - *l = sd->length; - - gtk_selection_data_free(sd); - - return 0; -} diff --git a/lassi-clipboard.h b/lassi-clipboard.h deleted file mode 100644 index f5574e3..0000000 --- a/lassi-clipboard.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef foolassiclipboardhfoo -#define foolassiclipboardhfoo - -#include - -typedef struct LassiClipboardInfo LassiClipboardInfo; -struct LassiServer; - -struct LassiClipboardInfo { - struct LassiServer *server; - - GtkClipboard *clipboard, *primary; -}; - -#include "lassi-server.h" - -int lassi_clipboard_init(LassiClipboardInfo *i, LassiServer *server); -void lassi_clipboard_done(LassiClipboardInfo *i); - -void lassi_clipboard_set(LassiClipboardInfo *i, gboolean primary, char *targets[]); -void lassi_clipboard_clear(LassiClipboardInfo *i, gboolean primary); -int lassi_clipboard_get(LassiClipboardInfo *i, gboolean primary, const char *target, int *format, gpointer *p, int *l); - -#endif diff --git a/lassi-grab.c b/lassi-grab.c deleted file mode 100644 index e2bda77..0000000 --- a/lassi-grab.c +++ /dev/null @@ -1,407 +0,0 @@ - -#include -#include - -#include -#include - -#include -#include - -#include "lassi-server.h" -#include "lassi-grab.h" - -#define TRIGGER_WIDTH 1 - -static int local2global(LassiGrabInfo *i, int y) { - g_assert(i); - g_assert(y >= 0 && y <= gdk_screen_get_height(i->screen)-1); - - /* Convert local screen coordinates (0 .. height) into global ones (0 . 65535) */ - return (y * 0xFFFF) / (gdk_screen_get_height(i->screen)-1); -} - -static int global2local(LassiGrabInfo *i, int y) { - g_assert(i); - g_assert(y >= 0 && y <= 0xFFFF); - - /* Convert global screen coordinates (0 . 65535) into local ones (0 .. height) */ - return (y * (gdk_screen_get_height(i->screen)-1)) / 0xFFFF; -} - -static void move_pointer(LassiGrabInfo *i, int x, int y) { - g_assert(i); - - /* Move the pointer ... */ - gdk_display_warp_pointer(i->display, i->screen, x, y); - - i->last_x = x; - i->last_y = y; -} - -static void drop_motion_events(LassiGrabInfo *i) { - XEvent txe; - - g_assert(i); - - /* Drop all queued motion events */ - while (XCheckTypedEvent(GDK_DISPLAY_XDISPLAY(i->display), MotionNotify, &txe)) - ; -} - -static int grab_input(LassiGrabInfo *i, GdkWindow *w) { - g_assert(i); - g_assert(w); - - if (gdk_pointer_grab(w, TRUE, - GDK_POINTER_MOTION_MASK| - GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, - NULL, i->empty_cursor, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) { - g_debug("pointer grab failed"); - return -1; - } - - - if (gdk_keyboard_grab(w, TRUE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) { - gdk_pointer_ungrab(GDK_CURRENT_TIME); - g_debug("keyboard grab failed"); - return -1; - } - - XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), False); - - if (i->grab_window != w) { - /* Now, rebase the pointer, so that we can easily calculate - * relative movements */ - move_pointer(i, i->base_x, i->base_y); - - i->grab_window = w; - - i->left_shift = i->right_shift = i->double_shift = FALSE; - - g_debug("Input now grabbed"); - } - - return 0; -} - -int lassi_grab_start(LassiGrabInfo *i, gboolean to_left) { - g_assert(i); - - return grab_input(i, to_left ? i->left_window : i->right_window); -} - -void lassi_grab_stop(LassiGrabInfo *i, int y) { - int x; - - g_assert(i); - - if (!i->grab_window) - return; - - /* Move the pointer back into our screen */ - if (y >= 0 && y < 0xFFFF) { - - /* We received a valid y coordinate, so let's use it */ - y = global2local(i, y); - - if (i->grab_window == i->left_window) - x = TRIGGER_WIDTH; - else - x = gdk_screen_get_width(i->screen)-TRIGGER_WIDTH-1; - - } else { - - /* We received an invlid y coordinate, so let's center the - * pointer */ - x = i->base_x; - y = i->base_y; - } - - move_pointer(i, x, y); - - gdk_keyboard_ungrab(GDK_CURRENT_TIME); - gdk_pointer_ungrab(GDK_CURRENT_TIME); - - drop_motion_events(i); - - i->grab_window = NULL; - - g_debug("Input now ungrabbed"); - - XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), True); -} - -static void handle_motion(LassiGrabInfo *i, int x, int y) { - int dx, dy; - int r; - int w, h; - - dx = x - i->last_x; - dy = y - i->last_y; - - i->last_x = x; - i->last_y = y; - -/* g_debug("rel motion %i %i", dx, dy); */ - - w = gdk_screen_get_width(i->screen); - h = gdk_screen_get_height(i->screen); - - if (x <= w/10 || y <= h/10 || - x >= (w*9)/10 || y >= (h*9)/10) { - - XEvent txe; - - /* Pointer is too near to the edges, move cursor - * back to center, so that further movements are - * not clipped */ - - g_debug("centering"); - - /* First, make sure there is no further motion event in the queue */ - while (XCheckTypedEvent(GDK_DISPLAY_XDISPLAY(i->display), MotionNotify, &txe)) { - dx += txe.xmotion.x - i->last_x; - dy += txe.xmotion.y - i->last_y; - - i->last_x = txe.xmotion.x; - i->last_y = txe.xmotion.y; - } - - move_pointer(i, i->base_x, i->base_y); - } - - /* Filter out non-existant or too large motions */ - if ((dx != 0 || dy != 0) && - ((abs(dx) <= (w*9)/20) && (abs(dy) <= (h*9)/20))) { - -/* g_debug("sending motion"); */ - - /* Send the event */ - r = lassi_server_motion_event(i->server, dx, dy); - g_assert(r >= 0); - } -} - -static GdkFilterReturn filter_func(GdkXEvent *gxe, GdkEvent *event, gpointer data) { - LassiGrabInfo *i = data; - XEvent *xe = (XEvent*) gxe; - GdkWindow *w = ((GdkEventAny*) event)->window; - - g_assert(i); - g_assert(xe); - g_assert(event); - - switch (xe->type){ - - case EnterNotify: { - XEnterWindowEvent *ewe = (XEnterWindowEvent*) xe; - - if (ewe->mode == NotifyNormal && ewe->state == 0 && !i->grab_window) { -/* g_debug("enter %u %u", ewe->x_root, ewe->y_root); */ - - /* Only honour this when no button/key is pressed */ - - if (lassi_server_change_grab(i->server, w == i->left_window, local2global(i, ewe->y_root)) >= 0) - grab_input(i, w); - - } else if (i->grab_window) - handle_motion(i, ewe->x_root, ewe->y_root); - - break; - } - - case MotionNotify: - - if (i->grab_window) { - XMotionEvent *me = (XMotionEvent*) xe; - -/* g_debug("motion %u %u", me->x_root, me->y_root); */ - handle_motion(i, me->x_root, me->y_root); - } - - break; - - case ButtonPress: - case ButtonRelease: - - if (i->grab_window) { - int r; - XButtonEvent *be = (XButtonEvent*) xe; - -/* g_debug("button press/release"); */ - handle_motion(i, be->x_root, be->y_root); - - /* Send the event */ - r = lassi_server_button_event(i->server, be->button, xe->type == ButtonPress); - g_assert(r >= 0); - } - break; - - case KeyPress: - case KeyRelease: - -/* g_debug("raw key"); */ - - if (i->grab_window) { - int r; - XKeyEvent *ke = (XKeyEvent *) xe; - KeySym keysym; - - keysym = XKeycodeToKeysym(GDK_DISPLAY_XDISPLAY(i->display), ke->keycode, 0); - - if (keysym == XK_Shift_L) - i->left_shift = ke->type == KeyPress; - if (keysym == XK_Shift_R) - i->right_shift = xe->type == KeyPress; - - if (i->left_shift && i->right_shift) - i->double_shift = TRUE; - -/* g_debug("left_shift=%i right_shift=%i 0x04%x", i->left_shift, i->right_shift, (unsigned) keysym); */ - -/* g_debug("key press/release"); */ - handle_motion(i, ke->x_root, ke->y_root); - - /* Send the event */ - r = lassi_server_key_event(i->server, keysym, xe->type == KeyPress); - g_assert(r >= 0); - - if (!i->left_shift && !i->right_shift && i->double_shift) { -/* g_debug("Got double shift"); */ - lassi_server_acquire_grab(i->server); - lassi_grab_stop(i, -1); - } - } - break; - } - - return GDK_FILTER_CONTINUE; -} - -int lassi_grab_init(LassiGrabInfo *i, LassiServer *s) { - GdkWindowAttr wa; - GdkColor black = { 0, 0, 0, 0 }; - const gchar cursor_data[1] = { 0 }; - GdkBitmap *bitmap; - int xtest_event_base, xtest_error_base; - int major_version, minor_version; - - memset(i, 0, sizeof(*i)); - i->server = s; - - i->screen = gdk_screen_get_default(); - i->display = gdk_screen_get_display(i->screen); - i->root = gdk_screen_get_root_window(i->screen); - - if (!XTestQueryExtension(GDK_DISPLAY_XDISPLAY(i->display), &xtest_event_base, &xtest_error_base, &major_version, &minor_version)) { - g_warning("XTest extension not supported."); - return -1; - } - - g_debug("XTest %u.%u supported.", major_version, minor_version); - - /* Create empty cursor */ - bitmap = gdk_bitmap_create_from_data(NULL, cursor_data, 1, 1); - i->empty_cursor = gdk_cursor_new_from_pixmap(bitmap, bitmap, &black, &black, 0, 0); - gdk_pixmap_unref(bitmap); - - /* Create trigger windows */ - memset(&wa, 0, sizeof(wa)); - - wa.title = "Mango Lassi Left"; - wa.event_mask = GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK; - wa.x = 0; - wa.y = gdk_screen_get_height(i->screen)/20; - wa.width = TRIGGER_WIDTH; - wa.height = (gdk_screen_get_height(i->screen)*18)/20; - wa.wclass = GDK_INPUT_ONLY; - wa.window_type = GDK_WINDOW_FOREIGN; - wa.override_redirect = TRUE; - wa.type_hint = GDK_WINDOW_TYPE_HINT_DOCK; - wa.cursor = i->empty_cursor; - - i->left_window = gdk_window_new(i->root, &wa, GDK_WA_TITLE|GDK_WA_X|GDK_WA_Y|GDK_WA_NOREDIR|GDK_WA_TYPE_HINT|GDK_WA_CURSOR); - gdk_window_set_keep_above(i->left_window, TRUE); - gdk_window_add_filter(i->left_window, filter_func, i); - - wa.title = "Mango Lassi Right"; - wa.x = gdk_screen_get_width(i->screen) - TRIGGER_WIDTH; - - i->right_window = gdk_window_new(i->root, &wa, GDK_WA_TITLE|GDK_WA_X|GDK_WA_Y|GDK_WA_NOREDIR|GDK_WA_TYPE_HINT); - gdk_window_set_keep_above(i->right_window, TRUE); - gdk_window_add_filter(i->right_window, filter_func, i); - - i->base_x = gdk_screen_get_width(i->screen)/2; - i->base_y = gdk_screen_get_height(i->screen)/2; - - XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), True); - - return 0; -} - -void lassi_grab_done(LassiGrabInfo *i) { - g_assert(i); - - lassi_grab_stop(i, -1); - - if (i->left_window) - gdk_window_destroy(i->left_window); - - if (i->right_window) - gdk_window_destroy(i->right_window); - - if (i->empty_cursor) - gdk_cursor_unref(i->empty_cursor); -} - -void lassi_grab_enable_triggers(LassiGrabInfo *i, gboolean left, gboolean right) { - g_assert(i); - - g_debug("Showing windows: left=%s, right=%s", left ? "yes" : "no", right ? "yes" : "no"); - - if (left) - gdk_window_show(i->left_window); - else - gdk_window_hide(i->left_window); - - if (right) - gdk_window_show(i->right_window); - else - gdk_window_hide(i->right_window); -} - -int lassi_grab_move_pointer_relative(LassiGrabInfo *i, int dx, int dy) { - g_assert(i); - - if (i->grab_window) - return -1; - - XTestFakeRelativeMotionEvent(GDK_DISPLAY_XDISPLAY(i->display), dx, dy, 0); - XSync(GDK_DISPLAY_XDISPLAY(i->display), False); - - return 0; -} - -int lassi_grab_press_button(LassiGrabInfo *i, unsigned button, gboolean is_press) { - g_assert(i); - - if (i->grab_window) - return -1; - - XTestFakeButtonEvent(GDK_DISPLAY_XDISPLAY(i->display), button, is_press, 0); - XSync(GDK_DISPLAY_XDISPLAY(i->display), False); - - return 0; -} - -int lassi_grab_press_key(LassiGrabInfo *i, unsigned key, gboolean is_press) { - g_assert(i); - if (i->grab_window) - return -1; - - XTestFakeKeyEvent(GDK_DISPLAY_XDISPLAY(i->display), XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(i->display), key), is_press, 0); - XSync(GDK_DISPLAY_XDISPLAY(i->display), False); - - return 0; -} diff --git a/lassi-grab.h b/lassi-grab.h deleted file mode 100644 index a9366e0..0000000 --- a/lassi-grab.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef foolassigrabhfoo -#define foolassigrabhfoo - -#include - -typedef struct LassiGrabInfo LassiGrabInfo; -struct LassiServer; - -struct LassiGrabInfo { - struct LassiServer *server; - - GdkDisplay *display; - GdkScreen *screen; - GdkWindow *root; - - GdkWindow *left_window, *right_window; - GdkCursor *empty_cursor; - GdkWindow *grab_window; - - int base_x, base_y; - int last_x, last_y; - - gboolean left_shift, right_shift, double_shift; -}; - -#include "lassi-server.h" - -int lassi_grab_init(LassiGrabInfo *i, LassiServer *server); -void lassi_grab_done(LassiGrabInfo *i); - -int lassi_grab_start(LassiGrabInfo *i, gboolean to_left); -void lassi_grab_stop(LassiGrabInfo *i, int y); - -void lassi_grab_enable_triggers(LassiGrabInfo *i, gboolean left, gboolean right); - -int lassi_grab_move_pointer_relative(LassiGrabInfo *i, int dx, int dy); -int lassi_grab_press_button(LassiGrabInfo *i, unsigned button, gboolean is_press); -int lassi_grab_press_key(LassiGrabInfo *i, unsigned key, gboolean is_press); - -#endif diff --git a/lassi-order.c b/lassi-order.c deleted file mode 100644 index ceeb3f1..0000000 --- a/lassi-order.c +++ /dev/null @@ -1,147 +0,0 @@ -#include - -#include - -#include "lassi-order.h" - -int lassi_list_compare(GList *i, GList *j) { - - for (; i && j; i = i->next, j = j->next) { - int c; - - c = strcmp(i->data, j->data); - - if (c) - return c; - - } - - if (i) - return 1; - - if (j) - return -1; - - return 0; -} - -gboolean lassi_list_nodups(GList *l) { - GList *i, *j; - - for (i = l; i; i = i->next) - for (j = i->next; j; j = j->next) - if (strcmp(i->data, j->data) == 0) - return FALSE; - - return TRUE; -} - -GList *lassi_list_merge(GList *a, GList *b) { - GList *ia, *ib, *p, *c, *d; - - g_assert(lassi_list_nodups(a)); - g_assert(lassi_list_nodups(b)); - - p = b; - - for (ia = a; ia; ia = ia->next) { - - for (ib = p; ib; ib = ib->next) { - - if (strcmp(ia->data, ib->data) == 0) { - - /* Found a common entry, hence copy everything since - * the last one we found from b to a */ - - for (c = p; c != ib; c = c->next) { - - /* Before we copy, make sure this entry is not yet - * in a */ - - for (d = a; d; d = d->next) - if (strcmp(c->data, d->data) == 0) - break; - - if (!d) - /* OK, This one is new, let's copy it */ - - a = g_list_insert_before(a, ia, g_strdup(c->data)); - } - - p = ib->next; - } - } - } - - /* Copy the tail */ - for (c = p; c; c = c->next) { - - for (d = a; d; d = d->next) - if (strcmp(c->data, d->data) == 0) - break; - - if (!d) - a = g_list_append(a, g_strdup(c->data)); - } - - g_assert(lassi_list_nodups(a)); - - return a; -} - -GList* lassi_list_copy(GList *l) { - GList *r = NULL; - - for (; l; l = l->next) - r = g_list_prepend(r, g_strdup(l->data)); - - return g_list_reverse(r); -} - -void lassi_list_free(GList *i) { - while (i) { - g_free(i->data); - i = i->next; - } -} - - -#if 0 - -int main(int argc, char *argv[]) { - GList *a = NULL, *b = NULL, *c = NULL, *d = NULL, *i; - - - a = g_list_append(a, "eins"); - a = g_list_append(a, "zwei"); - a = g_list_append(a, "vier"); - a = g_list_append(a, "fünf"); - a = g_list_append(a, "sechs"); - a = g_list_append(a, "acht"); - - b = g_list_append(b, "eins"); - b = g_list_append(b, "zwei"); - b = g_list_append(b, "drei"); - b = g_list_append(b, "vier"); - b = g_list_append(b, "sechs"); - b = g_list_append(b, "acht"); - - c = g_list_append(c, "eins"); - c = g_list_append(c, "sieben"); - c = g_list_append(c, "acht"); - - d = g_list_append(d, "drei"); - d = g_list_append(d, "neun"); - d = g_list_append(d, "zwei"); - - a = lassi_list_merge(a, b); - a = lassi_list_merge(a, c); - a = lassi_list_merge(a, d); - - for (i = a; i; i = i->next) - g_debug("%s", (char*) i->data); - - return 0; -} - -#endif diff --git a/lassi-order.h b/lassi-order.h deleted file mode 100644 index e41a0d7..0000000 --- a/lassi-order.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef foolassiorderhfoo -#define foolassiorderhfoo - -#include - -int lassi_list_compare(GList *i, GList *j); -gboolean lassi_list_nodups(GList *l); -GList *lassi_list_merge(GList *a, GList *b); -GList *lassi_list_copy(GList *l); -void lassi_list_free(GList *i); - -#endif - diff --git a/lassi-osd.c b/lassi-osd.c deleted file mode 100644 index ae9a21c..0000000 --- a/lassi-osd.c +++ /dev/null @@ -1,144 +0,0 @@ -#include -#include - -#include - -#include - -#include "lassi-osd.h" - -int lassi_osd_init(LassiOsdInfo *osd) { - GtkWidget *hbox; - GdkColor color; - guint32 cardinal; - GdkDisplay *display; - - g_assert(osd); - - memset(osd, 0, sizeof(*osd)); - - osd->window = gtk_window_new(GTK_WINDOW_POPUP); - gtk_window_set_title(GTK_WINDOW(osd->window), "Mango Lassi OSD"); - gtk_window_stick(GTK_WINDOW(osd->window)); - gtk_window_set_keep_above(GTK_WINDOW(osd->window), TRUE); - gtk_window_set_decorated(GTK_WINDOW(osd->window), FALSE); - gtk_window_set_deletable(GTK_WINDOW(osd->window), FALSE); - gtk_window_set_type_hint(GTK_WINDOW(osd->window), GDK_WINDOW_TYPE_HINT_NOTIFICATION); - gtk_window_set_skip_taskbar_hint(GTK_WINDOW(osd->window), TRUE); - gtk_window_set_skip_pager_hint(GTK_WINDOW(osd->window), TRUE); - gtk_window_set_accept_focus(GTK_WINDOW(osd->window), FALSE); - gtk_window_set_focus_on_map(GTK_WINDOW(osd->window), FALSE); - gtk_window_set_gravity(GTK_WINDOW(osd->window), GDK_GRAVITY_SOUTH_WEST); - - osd->label = gtk_label_new("Test"); - gtk_misc_set_padding(GTK_MISC(osd->label), 16, 0); -/* gtk_label_set_line_wrap(GTK_LABEL(osd->label), TRUE); */ - osd->left_icon = gtk_image_new(); - osd->right_icon = gtk_image_new(); - - hbox = gtk_hbox_new(0, 0); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 8); - - gtk_box_pack_start(GTK_BOX(hbox), osd->left_icon, FALSE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(hbox), osd->label, TRUE, TRUE, 0); - gtk_box_pack_end(GTK_BOX(hbox), osd->right_icon, FALSE, TRUE, 0); - - gtk_container_add(GTK_CONTAINER(osd->window), hbox); - - gtk_widget_show(hbox); - gtk_widget_show(osd->label); - - gdk_color_parse("#262624", &color); - if (!gdk_colormap_alloc_color(gtk_widget_get_colormap(osd->window), &color, FALSE, FALSE)) - gdk_color_black(gtk_widget_get_colormap(osd->window), &color); - gtk_widget_modify_bg(osd->window, GTK_STATE_NORMAL, &color); - - gtk_widget_realize(GTK_WIDGET(osd->window)); - - cardinal = 0xbfffffff; - display = gdk_drawable_get_display(osd->window->window); - XChangeProperty(GDK_DISPLAY_XDISPLAY(display), - GDK_WINDOW_XID(osd->window->window), - gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_WINDOW_OPACITY"), - XA_CARDINAL, 32, - PropModeReplace, - (guchar *) &cardinal, 1); - - g_debug("WINDOW=%p", osd->window); - - return 0; -} - -void lassi_osd_done(LassiOsdInfo *osd) { - g_assert(osd); - - gtk_widget_destroy(osd->window); - - memset(osd, 0, sizeof(*osd)); -} - -void lassi_osd_set_text(LassiOsdInfo *osd, const char *text, const char *icon_name_left, const char *icon_name_right) { - char *t; - int w, h, max_width; - - g_assert(osd); - g_assert(osd->window); - - g_debug("WINDOW=%p", osd->window); - - g_debug("Showing text '%s'", text); - - t = g_strdup_printf("%s", text); - gtk_label_set_markup(GTK_LABEL(osd->label), t); - g_free(t); - - if (icon_name_left) { - gtk_image_set_from_icon_name(GTK_IMAGE(osd->left_icon), icon_name_left, GTK_ICON_SIZE_DIALOG); - gtk_widget_show(osd->left_icon); - } else - gtk_widget_hide(osd->left_icon); - - if (icon_name_right) { - gtk_image_set_from_icon_name(GTK_IMAGE(osd->right_icon), icon_name_right, GTK_ICON_SIZE_DIALOG); - gtk_widget_show(osd->right_icon); - } else - gtk_widget_hide(osd->right_icon); - - max_width = (gdk_screen_width()*18)/20; - - g_debug("WINDOW=%p", osd->window); - - gtk_widget_set_size_request(osd->window, -1, -1); - - gtk_window_get_size(GTK_WINDOW(osd->window), &w, &h); - - g_debug("WINDOW=%p", osd->window); - - if (w > max_width) { - gtk_widget_set_size_request(osd->window, max_width, -1); - w = max_width; - } - - if (!icon_name_left == !icon_name_right) { - gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_CENTER); - gtk_window_move(GTK_WINDOW(osd->window), (gdk_screen_width() - w)/2, (gdk_screen_height()*9)/10 - h); - } else if (icon_name_left) { - gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_LEFT); - gtk_window_move(GTK_WINDOW(osd->window), gdk_screen_width()/20, (gdk_screen_height()*9)/10 - h); - } else { - gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_RIGHT); - gtk_window_move(GTK_WINDOW(osd->window), (gdk_screen_width()*19)/20 - w, (gdk_screen_height()*9)/10 - h); - } - - gtk_widget_show(osd->window); - - g_debug("osd shown"); -} - -void lassi_osd_hide(LassiOsdInfo *osd) { - g_assert(osd); - - gtk_widget_hide(osd->window); - - g_debug("osd hidden"); -} diff --git a/lassi-osd.h b/lassi-osd.h deleted file mode 100644 index 2a792eb..0000000 --- a/lassi-osd.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef foolassiosdhfoo -#define foolassiosdhfoo - -#include - -typedef struct LassiOsdInfo LassiOsdInfo; - -struct LassiOsdInfo { - GtkWidget *window, *label, *left_icon, *right_icon; -}; - -int lassi_osd_init(LassiOsdInfo *osd); -void lassi_osd_done(LassiOsdInfo *osd); - -void lassi_osd_set_text(LassiOsdInfo *osd, const char *text, const char *icon_name_left, const char *icon_name_right); -void lassi_osd_hide(LassiOsdInfo *osd); - -#endif diff --git a/lassi-prefs.c b/lassi-prefs.c deleted file mode 100644 index c3adf68..0000000 --- a/lassi-prefs.c +++ /dev/null @@ -1,212 +0,0 @@ -#include - -#include - -#include "lassi-prefs.h" -#include "lassi-server.h" - -enum { - COLUMN_ICON, - COLUMN_NAME, - COLUMN_GLIST, - N_COLUMNS -}; - - -static void on_add_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { - GtkWidget *d; - - d = aui_service_dialog_new("Choose Desktop to add", GTK_WINDOW(i->dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL); - aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(d), LASSI_SERVICE_TYPE, NULL); - - if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT) { - char a[AVAHI_ADDRESS_STR_MAX], *t; - - avahi_address_snprint(a, sizeof(a), aui_service_dialog_get_address(AUI_SERVICE_DIALOG(d))); - t = g_strdup_printf("tcp:port=%u,host=%s", aui_service_dialog_get_port(AUI_SERVICE_DIALOG(d)), a); - lassi_server_connect(i->server, t); - g_free(t); - } - - gtk_widget_destroy(d); -} - -static void on_remove_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { - GtkTreeSelection *selection; - GtkTreeIter iter; - char *id; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); - - if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) - return; - - gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); - if (id) { - lassi_server_disconnect(i->server, id, TRUE); - g_free(id); - } -} - -static void on_up_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { - GtkTreeSelection *selection; - GtkTreeIter iter; - char *id; - -/* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); - - if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) - return; - - gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); - - if (id) { - GList *o = lassi_list_copy(i->server->order); - lissi_list_move_up(o, id); - lassi_server_set_order(i->server, o); - g_free(id); - }*/ -} - -static void on_down_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { -} - -static void on_close_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { - gtk_widget_hide(GTK_WIDGET(i->dialog)); -} - -static void update_sensitive(LassiPrefsInfo *i) { - GtkTreeIter iter; - GtkTreePath *path; - gboolean is_first; - char *id; - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); - - if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { - gtk_widget_set_sensitive(i->up_button, FALSE); - gtk_widget_set_sensitive(i->down_button, FALSE); - gtk_widget_set_sensitive(i->remove_button, FALSE); - return; - } - - gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); - gtk_widget_set_sensitive(i->remove_button, strcmp(id, i->server->id) != 0); - g_free(id); - - path = gtk_tree_model_get_path(GTK_TREE_MODEL(i->list_store), &iter); - - is_first = gtk_tree_path_prev(path); - gtk_widget_set_sensitive(i->up_button, is_first); - if (is_first) - gtk_tree_path_next(path); - - gtk_tree_path_next(path); - gtk_widget_set_sensitive(i->down_button, gtk_tree_model_get_iter(GTK_TREE_MODEL(i->list_store), &iter, path)); - - gtk_tree_path_free(path); -} - -static void on_selection_changed(GtkTreeSelection *selection, LassiPrefsInfo *i) { - update_sensitive(i); -} - -int lassi_prefs_init(LassiPrefsInfo *i, LassiServer *server) { - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - - g_assert(i); - g_assert(server); - - memset(i, 0, sizeof(*i)); - i->server = server; - - i->xml = glade_xml_new("mango-lassi.glade", NULL, NULL); - - i->dialog = glade_xml_get_widget(i->xml, "preferences_dialog"); - i->up_button = glade_xml_get_widget(i->xml, "up_button"); - i->down_button = glade_xml_get_widget(i->xml, "down_button"); - i->add_button = glade_xml_get_widget(i->xml, "add_button"); - i->remove_button = glade_xml_get_widget(i->xml, "remove_button"); - i->tree_view = glade_xml_get_widget(i->xml, "tree_view"); - - glade_xml_signal_connect_data(i->xml, "on_add_button_clicked", (GCallback) on_add_button_clicked, i); - glade_xml_signal_connect_data(i->xml, "on_remove_button_clicked", (GCallback) on_remove_button_clicked, i); - glade_xml_signal_connect_data(i->xml, "on_up_button_clicked", (GCallback) on_up_button_clicked, i); - glade_xml_signal_connect_data(i->xml, "on_down_button_clicked", (GCallback) on_down_button_clicked, i); - - glade_xml_signal_connect_data(i->xml, "on_close_button_clicked", (GCallback) on_close_button_clicked, i); - - g_signal_connect(G_OBJECT(i->dialog), "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); - - i->list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); - gtk_tree_view_set_model(GTK_TREE_VIEW(i->tree_view), GTK_TREE_MODEL(i->list_store)); - - column = gtk_tree_view_column_new_with_attributes("Icon", gtk_cell_renderer_pixbuf_new(), "icon-name", COLUMN_ICON, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(i->tree_view), column); - - column = gtk_tree_view_column_new_with_attributes("Name", gtk_cell_renderer_text_new(), "text", COLUMN_NAME, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(i->tree_view), column); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(on_selection_changed), i); - - lassi_prefs_update(i); - - return 0; -} - -void lassi_prefs_update(LassiPrefsInfo *i) { - GList *l; - char *selected_item = NULL; - GtkTreeSelection *selection; - GtkTreeIter iter; - - g_assert(i); - - g_message("prefs update"); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); - - if (gtk_tree_selection_get_selected(selection, NULL, &iter)) - gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &selected_item, -1); - - gtk_list_store_clear(GTK_LIST_STORE(i->list_store)); - - for (l = i->server->order; l; l = l->next) { - - if (!lassi_server_is_connected(i->server, l->data)) - continue; - - gtk_list_store_append(GTK_LIST_STORE(i->list_store), &iter); - gtk_list_store_set(GTK_LIST_STORE(i->list_store), &iter, - COLUMN_ICON, strcmp(i->server->id, l->data) ? "network-wired" : "user-desktop", - COLUMN_NAME, l->data, - COLUMN_GLIST, l, -1); - - if (selected_item) - if (strcmp(selected_item, l->data) == 0) - gtk_tree_selection_select_iter(selection, &iter); - } - - g_free(selected_item); - - update_sensitive(i); -} - -void lassi_prefs_show(LassiPrefsInfo *i) { - g_assert(i); - - gtk_window_present(GTK_WINDOW(i->dialog)); -} - -void lassi_prefs_done(LassiPrefsInfo *i) { - g_assert(i); - - g_object_unref(G_OBJECT(i->xml)); - g_object_unref(G_OBJECT(i->list_store)); - - memset(i, 0, sizeof(*i)); -} diff --git a/lassi-prefs.h b/lassi-prefs.h deleted file mode 100644 index a948709..0000000 --- a/lassi-prefs.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef foolassiprefshfoo -#define foolassiprefshfoo - -#include -#include - -typedef struct LassiPrefsInfo LassiPrefsInfo; -struct LassiServer; - -struct LassiPrefsInfo { - struct LassiServer *server; - - GtkWidget *dialog; - GtkWidget *up_button, *down_button, *add_button, *remove_button; - GtkWidget *tree_view; - - GtkListStore *list_store; - - GladeXML *xml; -}; - -#include "lassi-server.h" - -int lassi_prefs_init(LassiPrefsInfo *i, LassiServer *server); -void lassi_prefs_show(LassiPrefsInfo *i); -void lassi_prefs_update(LassiPrefsInfo *i); -void lassi_prefs_done(LassiPrefsInfo *i); - -#endif diff --git a/lassi-server.c b/lassi-server.c deleted file mode 100644 index f160279..0000000 --- a/lassi-server.c +++ /dev/null @@ -1,1554 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "lassi-server.h" -#include "lassi-grab.h" -#include "lassi-order.h" -#include "lassi-clipboard.h" -#include "lassi-avahi.h" -#include "lassi-tray.h" - -#define LASSI_INTERFACE "org.gnome.MangoLassi" - -#define PORT_MIN 7421 -#define PORT_MAX (PORT_MIN + 50) - -#define CONNECTIONS_MAX 16 - -static void server_disconnect_all(LassiServer *ls, gboolean clear_order); -static void server_send_update_grab(LassiServer *ls, int y); - -static void server_broadcast(LassiServer *ls, DBusMessage *m, LassiConnection *except) { - GList *i; - - g_assert(ls); - g_assert(m); - - for (i = ls->connections; i; i = i->next) { - dbus_bool_t b; - LassiConnection *lc = i->data; - DBusMessage *n; - - if (lc == except || !lc->id) - continue; - - n = dbus_message_copy(m); - g_assert(n); - b = dbus_connection_send(lc->dbus_connection, n, NULL); - g_assert(b); - dbus_message_unref(n); - } -} - -static void server_layout_changed(LassiServer *ls, int y) { - g_assert(ls); - - g_debug("updating layout"); - - lassi_grab_enable_triggers(&ls->grab_info, !!ls->connections_left, !!ls->connections_right); - - if (ls->active_connection) { - char *t; - gboolean to_left = !!g_list_find(ls->connections_left, ls->active_connection); - - t = g_strdup_printf("Mouse and keyboard are being redirected to %s, which is located to the %s of this screen.\n" - "To redirect input back to this screen, press and release both shift keys simultaneously.", - ls->active_connection->id, to_left ? "left" : "right"); - - if (to_left) - lassi_osd_set_text(&ls->osd_info, t, "go-previous", NULL); - else - lassi_osd_set_text(&ls->osd_info, t, NULL, "go-next"); - - lassi_grab_start(&ls->grab_info, to_left); - - } else { - lassi_grab_stop(&ls->grab_info, y); - lassi_osd_hide(&ls->osd_info); - } -} - -static void server_set_order(LassiServer *ls, GList *order) { - GList *l; - gboolean on_left = TRUE; - g_assert(ls); - - lassi_list_free(ls->order); - ls->order = order; - - g_list_free(ls->connections_left); - g_list_free(ls->connections_right); - - ls->connections_left = ls->connections_right = NULL; - - for (l = ls->order; l; l = l->next) { - LassiConnection *lc; - - if (!(lc = g_hash_table_lookup(ls->connections_by_id, l->data))) { - - if (strcmp(ls->id, l->data)) - continue; - } - - g_assert(lc || on_left); - - if (!lc) - on_left = FALSE; - else if (on_left) - ls->connections_left = g_list_prepend(ls->connections_left, lc); - else - ls->connections_right = g_list_prepend(ls->connections_right, lc); - } - - for (l = ls->connections; l; l = l->next) { - LassiConnection *lc = l->data; - - if (!lc->id) - continue; - - if (g_list_find(ls->connections_left, lc)) - continue; - - if (g_list_find(ls->connections_right, lc)) - continue; - - ls->order = g_list_append(ls->order, lc->id); - ls->connections_right = g_list_prepend(ls->connections_right, lc); - } - - ls->connections_right = g_list_reverse(ls->connections_right); - server_layout_changed(ls, -1); - - lassi_prefs_update(&ls->prefs_info); -} - -static void server_dump(LassiServer *ls) { - GList *l; - int n = 0; - - g_assert(ls); - - g_debug("BEGIN Current connections:"); - - g_debug("Displays left of us:"); - for (l = ls->connections_left; l; l = l->next) { - LassiConnection *lc = l->data; - if (!lc->id) - continue; - g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address); - } - - g_debug("Displays right of us:"); - for (l = ls->connections_right; l; l = l->next) { - LassiConnection *lc = l->data; - if (!lc->id) - continue; - g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address); - } - - if (!ls->active_connection) - g_debug("We're the active connection"); - - g_debug("END"); -} - -static void connection_destroy(LassiConnection *lc) { - g_assert(lc); - - dbus_connection_flush(lc->dbus_connection); - dbus_connection_close(lc->dbus_connection); - dbus_connection_unref(lc->dbus_connection); - g_free(lc->id); - g_free(lc->address); - g_free(lc); -} - -static void server_pick_active_connection(LassiServer *ls) { - LassiConnection *pick; - GList *l; - char *id; - - pick = NULL; - id = ls->id; - - for (l = ls->connections; l; l = l->next) { - LassiConnection *lc = l->data; - - if (!lc->id) - continue; - - if (strcmp(lc->id, id) > 0) { - id = lc->id; - pick = lc; - } - } - - ls->active_connection = pick; - - server_send_update_grab(ls, -1); - server_layout_changed(ls, -1); -} - -static void server_send_update_grab(LassiServer *ls, int y) { - char *active; - DBusMessage *n; - dbus_bool_t b; - gint32 g; - - g_assert(ls); - - active = ls->active_connection ? ls->active_connection->id : ls->id; - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateGrab"); - g_assert(n); - - g = ++ ls->active_generation; - b = dbus_message_append_args( - n, - DBUS_TYPE_INT32, &g, - DBUS_TYPE_STRING, &active, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - g_assert(b); - - server_broadcast(ls, n, NULL); - dbus_message_unref(n); -} - -static void server_send_update_order(LassiServer *ls, LassiConnection *except) { - DBusMessage *n; - dbus_bool_t b; - gint32 g; - DBusMessageIter iter, sub; - GList *l; - - g_assert(ls); - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateOrder"); - g_assert(n); - - g = ++ ls->order_generation; - b = dbus_message_append_args( - n, - DBUS_TYPE_INT32, &g, - DBUS_TYPE_INVALID); - g_assert(b); - - dbus_message_iter_init_append(n, &iter); - - b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub); - g_assert(b); - - for (l = ls->order; l; l = l->next) { - b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &l->data); - g_assert(b); - } - - b = dbus_message_iter_close_container(&iter, &sub); - g_assert(b); - - server_broadcast(ls, n, except); - dbus_message_unref(n); -} - -int lassi_server_change_grab(LassiServer *ls, gboolean to_left, int y) { - LassiConnection *lc; - GList *l; - - g_assert(ls); - - l = to_left ? ls->connections_left : ls->connections_right; - lc = l ? l->data : NULL; - - if (!lc) - return -1; - - ls->active_connection = lc; - - server_send_update_grab(ls, y); - server_layout_changed(ls, y); - return 0; -} - -int lassi_server_acquire_grab(LassiServer *ls) { - g_assert(ls); - - ls->active_connection = NULL; - - server_send_update_grab(ls, -1); - server_layout_changed(ls, -1); - return 0; -} - -int lassi_server_motion_event(LassiServer *ls, int dx, int dy) { - DBusMessage *n; - dbus_bool_t b; - - g_assert(ls); - - if (!ls->active_connection) - return -1; - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "MotionEvent"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID); - g_assert(b); - - b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); - g_assert(b); - - dbus_message_unref(n); - - dbus_connection_flush(ls->active_connection->dbus_connection); - - return 0; -} - -int lassi_server_button_event(LassiServer *ls, unsigned button, gboolean is_press) { - DBusMessage *n; - dbus_bool_t b; - - if (!ls->active_connection) - return -1; - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "ButtonEvent"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID); - g_assert(b); - - b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); - g_assert(b); - - dbus_message_unref(n); - - dbus_connection_flush(ls->active_connection->dbus_connection); - - return 0; -} - -int lassi_server_key_event(LassiServer *ls, unsigned key, gboolean is_press) { - DBusMessage *n; - dbus_bool_t b; - - if (!ls->active_connection) - return -1; - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "KeyEvent"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID); - g_assert(b); - - b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); - g_assert(b); - - dbus_message_unref(n); - - dbus_connection_flush(ls->active_connection->dbus_connection); - - return 0; -} - -static void show_welcome(LassiConnection *lc, gboolean connect) { - gboolean to_left; - LassiServer *ls; - char *summary, *body; - - g_assert(lc); - - ls = lc->server; - to_left = !!g_list_find(ls->connections_left, lc); - - if (connect) { - summary = g_strdup_printf("%s now shares input with this desktop", lc->id); - body = g_strdup_printf("You're now sharing keyboard and mouse with %s which is located to the %s.", lc->id, to_left ? "left" : "right"); - } else { - summary = g_strdup_printf("%s no longer shares input with this desktop", lc->id); - body = g_strdup_printf("You're no longer sharing keyboard and mouse with %s which was located to the %s.", lc->id, to_left ? "left" : "right"); - } - - lassi_tray_show_notification(&ls->tray_info, summary, body, to_left ? LASSI_TRAY_NOTIFICATION_LEFT : LASSI_TRAY_NOTIFICATION_RIGHT); - - g_free(summary); - g_free(body); -} - -static void connection_unlink(LassiConnection *lc, gboolean remove_from_order) { - LassiServer *ls; - g_assert(lc); - - g_debug("Unlinking %s (%s)", lc->id, lc->address); - - ls = lc->server; - - if (lc->id) { - DBusMessage *n; - dbus_bool_t b; - - /* Tell everyone */ - n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved"); - g_assert(n); - - b = dbus_message_append_args(n, - DBUS_TYPE_STRING, &lc->id, - DBUS_TYPE_STRING, &lc->address, - DBUS_TYPE_BOOLEAN, &remove_from_order, - DBUS_TYPE_INVALID); - g_assert(b); - - server_broadcast(ls, n, NULL); - dbus_message_unref(n); - } - - ls->connections = g_list_remove(ls->connections, lc); - ls->n_connections --; - - if (lc->id) { - show_welcome(lc, FALSE); - - g_hash_table_remove(ls->connections_by_id, lc->id); - ls->connections_left = g_list_remove(ls->connections_left, lc); - ls->connections_right = g_list_remove(ls->connections_right, lc); - - if (ls->active_connection == lc) - server_pick_active_connection(ls); - - if (ls->clipboard_connection == lc) { - ls->clipboard_connection = NULL; - ls->clipboard_empty = TRUE; - lassi_clipboard_clear(&lc->server->clipboard_info, FALSE); - } - - if (ls->primary_connection == lc) { - ls->primary_connection = NULL; - ls->primary_empty = TRUE; - lassi_clipboard_clear(&lc->server->clipboard_info, TRUE); - } - - if (remove_from_order) { - GList *i = g_list_find_custom(ls->order, lc->id, (GCompareFunc) strcmp); - - if (i) - ls->order = g_list_delete_link(ls->order, i); - } - - server_layout_changed(ls, -1); - lassi_prefs_update(&ls->prefs_info); - server_dump(ls); - } - - lassi_tray_update(&ls->tray_info, ls->n_connections); - - connection_destroy(lc); -} - -static void server_position_connection(LassiServer *ls, LassiConnection *lc) { - GList *l; - LassiConnection *last = NULL; - - g_assert(ls); - g_assert(lc); - - g_assert(!g_list_find(ls->connections_left, lc)); - g_assert(!g_list_find(ls->connections_right, lc)); - - for (l = ls->order; l; l = l->next) { - LassiConnection *k; - - if (strcmp(l->data, lc->id) == 0) - break; - - if ((k = g_hash_table_lookup(ls->connections_by_id, l->data))) - last = k; - } - - if (l) { - /* OK, We found a spot to add this */ - - if (last) { - GList *j; - - /*Ok, this one belongs to the right of 'last' */ - - if ((j = g_list_find(ls->connections_left, last))) - /* This one belongs in the left list */ - ls->connections_left = g_list_insert_before(ls->connections_left, j, lc); - else { - /* This one belongs in the rightlist */ - ls->connections_right = g_list_reverse(ls->connections_right); - j = g_list_find(ls->connections_right, last); - g_assert(j); - ls->connections_right = g_list_insert_before(ls->connections_right, j, lc); - ls->connections_right = g_list_reverse(ls->connections_right); - } - } else - /* Hmm, this is before the left end */ - ls->connections_left = g_list_append(ls->connections_left, lc); - } else { - ls->order = g_list_append(ls->order, g_strdup(lc->id)); - /* No spot found, let's add it to the right end */ - ls->connections_right = g_list_append(ls->connections_right, lc); - } -} - -int lassi_server_acquire_clipboard(LassiServer *ls, gboolean primary, char**targets) { - DBusMessageIter iter, sub; - DBusMessage *n; - gint32 g; - gboolean b; - - g_assert(ls); - g_assert(targets); - - if (primary) { - ls->primary_empty = FALSE; - ls->primary_connection = NULL; - } else { - ls->clipboard_empty = FALSE; - ls->clipboard_connection = NULL; - } - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "AcquireClipboard"); - g_assert(n); - - if (primary) - g = ++ ls->primary_generation; - else - g = ++ ls->clipboard_generation; - - b = dbus_message_append_args(n, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID); - g_assert(b); - - dbus_message_iter_init_append(n, &iter); - - b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub); - g_assert(b); - - for (; *targets; targets++) { - g_debug("Exporting target %s", *targets); - b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, targets); - g_assert(b); - } - - b = dbus_message_iter_close_container(&iter, &sub); - g_assert(b); - - server_broadcast(ls, n, NULL); - g_assert(b); - - dbus_message_unref(n); - return 0; -} - -int lassi_server_return_clipboard(LassiServer *ls, gboolean primary) { - DBusMessage *n; - guint32 g; - gboolean b; - - g_assert(ls); - - if (primary) { - - if (ls->primary_empty || ls->primary_connection != NULL) - return -1; - - ls->primary_empty = TRUE; - ls->primary_connection = NULL; - - } else { - - if (ls->clipboard_empty || ls->clipboard_connection != NULL) - return -1; - - ls->clipboard_empty = TRUE; - ls->clipboard_connection = NULL; - } - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "ReturnClipboard"); - g_assert(n); - - if (primary) - g = ++ ls->primary_generation; - else - g = ++ ls->clipboard_generation; - - b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID); - g_assert(b); - - server_broadcast(ls, n, NULL); - - dbus_message_unref(n); - return 0; -} - -int lassi_server_get_clipboard(LassiServer *ls, gboolean primary, const char *t, int *f, gpointer *p, int *l) { - DBusMessage *n, *reply; - DBusConnection *c; - DBusError e; - int ret = -1; - DBusMessageIter iter, sub; - gboolean b; - - g_assert(ls); - - dbus_error_init(&e); - - if (primary) { - - if (ls->primary_empty || !ls->primary_connection) - return -1; - - c = ls->primary_connection->dbus_connection; - - } else { - - if (ls->clipboard_empty || !ls->clipboard_connection) - return -1; - - c = ls->clipboard_connection->dbus_connection; - } - - n = dbus_message_new_method_call(NULL, "/", LASSI_INTERFACE, "GetClipboard"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &t, DBUS_TYPE_INVALID); - g_assert(b); - - if (!(reply = dbus_connection_send_with_reply_and_block(c, n, -1, &e))) { - g_debug("Getting clipboard failed: %s", e.message); - goto finish; - } - - dbus_message_iter_init(reply, &iter); - dbus_message_iter_get_basic(&iter, f); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE) { - g_debug("Invalid clipboard data"); - goto finish; - } - - dbus_message_iter_recurse(&iter, &sub); - dbus_message_iter_get_fixed_array(&sub, p, l); - - *p = g_memdup(*p, *l); - - ret = 0; - -finish: - - dbus_message_unref(n); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&e); - - return ret; -} - -static int signal_hello(LassiConnection *lc, DBusMessage *m) { - const char *id, *address; - DBusError e; - GList *i; - dbus_bool_t b; - DBusMessage *n; - gint32 active_generation, order_generation, clipboard_generation; - - dbus_error_init(&e); - - if (lc->id) { - g_debug("Received duplicate HelloNode."); - return -1; - } - - if (!(dbus_message_get_args( - m, &e, - DBUS_TYPE_STRING, &id, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INT32, &active_generation, - DBUS_TYPE_INT32, &order_generation, - DBUS_TYPE_INT32, &clipboard_generation, - DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if (strcmp(id, lc->server->id) == 0) { - g_debug("Dropping looped back connection."); - return -1; - } - - if (g_hash_table_lookup(lc->server->connections_by_id, id)) { - g_debug("Dropping duplicate connection."); - return -1; - } - - lc->server->active_generation = MAX(lc->server->active_generation, active_generation); - lc->server->order_generation = MAX(lc->server->order_generation, order_generation); - lc->server->clipboard_generation = MAX(lc->server->clipboard_generation, clipboard_generation); - - g_debug("Got welcome from %s (%s)", id, address); - - lc->id = g_strdup(id); - lc->address = g_strdup(address); - g_hash_table_insert(lc->server->connections_by_id, lc->id, lc); - server_position_connection(lc->server, lc); - - /* Notify all old nodes of the new one */ - n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); - g_assert(b); - - server_broadcast(lc->server, n, lc); - dbus_message_unref(n); - - /* Notify new node about old nodes */ - for (i = lc->server->connections; i; i = i->next) { - LassiConnection *k = i->data; - dbus_bool_t b; - - if (k == lc || !k->id) - continue; - - n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); - g_assert(b); - - b = dbus_connection_send(lc->dbus_connection, n, NULL); - g_assert(b); - - dbus_message_unref(n); - } - - if (lc->we_are_client) { - server_send_update_grab(lc->server, -1); - server_send_update_order(lc->server, NULL); - - lc->delayed_welcome = FALSE; - show_welcome(lc, TRUE); - } else - lc->delayed_welcome = TRUE; - - server_layout_changed(lc->server, -1); - lassi_prefs_update(&lc->server->prefs_info); - - server_dump(lc->server); - - return 0; -} - -static int signal_node_added(LassiConnection *lc, DBusMessage *m) { - const char *id, *address; - DBusError e; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if (strcmp(id, lc->server->id) == 0) - return 0; - - if (g_hash_table_lookup(lc->server->connections_by_id, id)) - return 0; - - if (!(lassi_server_connect(lc->server, address))) { - DBusMessage *n; - dbus_bool_t b; - - /* Failed to connnect to this client, tell everyone */ - n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved"); - g_assert(n); - - b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); - g_assert(b); - - server_broadcast(lc->server, n, NULL); - dbus_message_unref(n); - } - - return 0; -} - -static int signal_node_removed(LassiConnection *lc, DBusMessage *m) { - const char *id, *address; - DBusError e; - LassiConnection *k; - gboolean remove_from_order; - LassiServer *ls; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, - DBUS_TYPE_STRING, &id, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_BOOLEAN, &remove_from_order, - DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if (strcmp(id, lc->server->id) == 0) { - g_debug("We've been kicked ourselves."); - - server_disconnect_all(lc->server, TRUE); - return 0; - } - - if (remove_from_order) { - GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); - - if (i) - ls->order = g_list_delete_link(ls->order, i); - } - - ls = lc->server; - - if ((k = g_hash_table_lookup(lc->server->connections_by_id, id))) - connection_unlink(k, remove_from_order); - - server_broadcast(ls, m, lc == k ? NULL : lc); - - return 0; -} - -static int signal_update_grab(LassiConnection *lc, DBusMessage *m) { - const char*id, *current_id; - gint32 generation; - LassiConnection *k = NULL; - DBusError e; - int y; - - dbus_error_init(&e); - - if (!(dbus_message_get_args( - m, &e, - DBUS_TYPE_INT32, &generation, - DBUS_TYPE_STRING, &id, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - g_debug("received grab request for %s (%i vs %i)", id, lc->server->active_generation, generation); - - if (strcmp(id, lc->server->id) && !(k = g_hash_table_lookup(lc->server->connections_by_id, id))) { - g_debug("Unknown connection"); - return -1; - } - - if (k == lc->server->active_connection) { - g_debug("Connection already active"); - return 0; - } - - current_id = lc->server->active_connection ? lc->server->active_connection->id : lc->server->id; - - if ((lc->server->active_generation > generation || (lc->server->active_generation == generation && strcmp(current_id, id) > 0))) { - g_debug("Ignoring request for active connection"); - return 0; - } - - lc->server->active_connection = k; - lc->server->active_generation = generation; - - if (!k) - g_debug("We're now the active server."); - else - g_debug("Connection '%s' activated.", k->id); - - server_broadcast(lc->server, m, lc); - server_layout_changed(lc->server, y); - - return 0; -} - -static int signal_update_order(LassiConnection *lc, DBusMessage *m) { - gint32 generation; - DBusError e; - DBusMessageIter iter, sub; - GList *new_order = NULL, *merged_order = NULL; - int r = 0; - int c = 0; - - dbus_error_init(&e); - - if (!(dbus_message_get_args( - m, &e, - DBUS_TYPE_INT32, &generation, - DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - dbus_message_iter_init(m, &iter); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) { - g_debug("Bad connection list fo the left"); - return -1; - } - - if (lc->server->order_generation > generation) { - g_debug("Ignoring request for layout"); - return 0; - } - - dbus_message_iter_recurse(&iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *id; - dbus_message_iter_get_basic(&sub, &id); - new_order = g_list_prepend(new_order, g_strdup(id)); - dbus_message_iter_next(&sub); - } - - new_order = g_list_reverse(new_order); - - if (!lassi_list_nodups(new_order)) { - g_debug("Received invalid list."); - r = -1; - goto finish; - } - - c = lassi_list_compare(lc->server->order, new_order); - - if (c == 0) { - g_debug("Requested order identical to ours."); - goto finish; - } - - if (lc->server->order_generation == generation && c > 0) { - g_debug("Ignoring request for layout 2"); - goto finish; - } - - merged_order = lassi_list_merge(lassi_list_copy(new_order), lc->server->order); - - if (lassi_list_compare(lc->server->order, merged_order)) { - server_set_order(lc->server, merged_order); - merged_order = NULL; - } - - server_send_update_order(lc->server, lassi_list_compare(lc->server->order, new_order) ? NULL : lc); - - lc->server->order_generation = generation; - -finish: - - lassi_list_free(new_order); - lassi_list_free(merged_order); - - if (lc->delayed_welcome) { - lc->delayed_welcome = FALSE; - show_welcome(lc, TRUE); - } - - return r; -} - -static int signal_key_event(LassiConnection *lc, DBusMessage *m) { - DBusError e; - guint32 key; - gboolean is_press; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - -/* g_debug("got dbus key %i %i", key, !!is_press); */ - lassi_grab_press_key(&lc->server->grab_info, key, is_press); - - return 0; -} - -static int signal_motion_event(LassiConnection *lc, DBusMessage *m) { - DBusError e; - int dx, dy; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - -/* g_debug("got dbus motion %i %i", dx, dy); */ - lassi_grab_move_pointer_relative(&lc->server->grab_info, dx, dy); - - return 0; -} - -static int signal_button_event(LassiConnection *lc, DBusMessage *m) { - DBusError e; - guint32 button; - gboolean is_press; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - -/* g_debug("got dbus button %i %i", button, !!is_press); */ - lassi_grab_press_button(&lc->server->grab_info, button, is_press); - - return 0; -} - -static int signal_acquire_clipboard(LassiConnection *lc, DBusMessage *m) { - DBusError e; - gint32 g; - gboolean primary; - DBusMessageIter iter, sub; - char **targets; - unsigned alloc_targets, j; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) { - g_debug("Ignoring request for clipboard."); - return 0; - } - - /* FIXME, tie break missing */ - - dbus_message_iter_init(m, &iter); - dbus_message_iter_next(&iter); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) { - g_debug("Bad target list"); - return -1; - } - - dbus_message_iter_recurse(&iter, &sub); - - targets = g_new(char*, alloc_targets = 20); - j = 0; - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *t; - dbus_message_iter_get_basic(&sub, &t); - - if (j >= alloc_targets) { - alloc_targets *= 2; - targets = g_realloc(targets, sizeof(char*) * (alloc_targets+1)); - } - - g_assert(j < alloc_targets); - - targets[j++] = (char*) t; - dbus_message_iter_next(&sub); - - g_debug("Received target %s on %s", t, lc->id); - } - - targets[j] = NULL; - - lassi_clipboard_set(&lc->server->clipboard_info, primary, targets); - - g_free(targets); - - if (primary) { - lc->server->primary_connection = lc; - lc->server->primary_empty = FALSE; - lc->server->primary_generation = g; - } else { - lc->server->clipboard_connection = lc; - lc->server->clipboard_empty = FALSE; - lc->server->clipboard_generation = g; - } - - return 0; -} - -static int signal_return_clipboard(LassiConnection *lc, DBusMessage *m) { - DBusError e; - gint32 g; - gboolean primary; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) { - g_debug("Ignoring request for clipboard empty."); - return 0; - } - - /* FIXME, tie break missing */ - - lassi_clipboard_clear(&lc->server->clipboard_info, primary); - - if (primary) { - lc->server->primary_connection = NULL; - lc->server->primary_empty = TRUE; - lc->server->primary_generation = g; - } else { - lc->server->clipboard_connection = NULL; - lc->server->clipboard_empty = TRUE; - lc->server->clipboard_generation = g; - } - - return 0; -} - -static int method_get_clipboard(LassiConnection *lc, DBusMessage *m) { - DBusError e; - char *type; - gboolean primary; - DBusMessage *n = NULL; - gint32 f; - gpointer p = NULL; - int l = 0; - DBusMessageIter iter, sub; - gboolean b; - - dbus_error_init(&e); - - if (!(dbus_message_get_args(m, &e, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID))) { - g_debug("Received invalid message: %s", e.message); - dbus_error_free(&e); - return -1; - } - - if ((primary && (lc->server->primary_connection || lc->server->primary_empty)) || - (!primary && (lc->server->clipboard_connection || lc->server->clipboard_empty))) { - n = dbus_message_new_error(m, LASSI_INTERFACE ".NotOwner", "We're not the clipboard owner"); - goto finish; - } - - if (lassi_clipboard_get(&lc->server->clipboard_info, primary, type, &f, &p, &l) < 0) { - n = dbus_message_new_error(m, LASSI_INTERFACE ".ClipboardFailure", "Failed to read clipboard data"); - goto finish; - } - - if (l > dbus_connection_get_max_message_size(lc->dbus_connection)*9/10) { - n = dbus_message_new_error(m, LASSI_INTERFACE ".TooLarge", "Clipboard data too large"); - goto finish; - } - - n = dbus_message_new_method_return(m); - g_assert(n); - - dbus_message_iter_init_append(n, &iter); - b = dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &f); - g_assert(b); - - b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub); - g_assert(b); - - b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, l); - g_assert(b); - - b = dbus_message_iter_close_container(&iter, &sub); - g_assert(b); - -finish: - g_assert(n); - - dbus_connection_send(lc->dbus_connection, n, NULL); - dbus_message_unref(n); - - g_free(p); - - return 0; -} - -DBusHandlerResult message_function(DBusConnection *c, DBusMessage *m, void *userdata) { - DBusError e; - LassiConnection *lc = userdata; - - g_assert(c); - g_assert(m); - g_assert(lc); - - dbus_error_init(&e); - -/* g_debug("[%s] interface=%s, path=%s, member=%s serial=%u", */ -/* lc->id, */ -/* dbus_message_get_interface(m), */ -/* dbus_message_get_path(m), */ -/* dbus_message_get_member(m), */ -/* dbus_message_get_serial(m)); */ - - if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) - goto fail; - - else if (dbus_message_is_signal(m, LASSI_INTERFACE, "Hello")) { - if (signal_hello(lc, m) < 0) - goto fail; - - } else if (lc->id) { - - if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeAdded")) { - - if (signal_node_added(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeRemoved")) { - - if (signal_node_removed(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateGrab")) { - - if (signal_update_grab(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateOrder")) { - - if (signal_update_order(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "KeyEvent")) { - - if (signal_key_event(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "MotionEvent")) { - - if (signal_motion_event(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ButtonEvent")) { - - if (signal_button_event(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "AcquireClipboard")) { - - if (signal_acquire_clipboard(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ReturnClipboard")) { - - if (signal_return_clipboard(lc, m) < 0) - goto fail; - - } else if (dbus_message_is_method_call(m, LASSI_INTERFACE, "GetClipboard")) { - - if (method_get_clipboard(lc, m) < 0) - goto fail; - - } else - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - } else - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - return DBUS_HANDLER_RESULT_HANDLED; - -fail: - - dbus_error_free(&e); - - connection_unlink(lc, TRUE); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static LassiConnection* connection_add(LassiServer *ls, DBusConnection *c, gboolean we_are_client) { - LassiConnection *lc; - dbus_bool_t b; - DBusMessage *m; - gint32 ag, og, cg; - int fd, one = 1; - - g_assert(ls); - g_assert(c); - - lc = g_new(LassiConnection, 1); - lc->dbus_connection = dbus_connection_ref(c); - lc->server = ls; - lc->id = lc->address = NULL; - lc->we_are_client = we_are_client; - lc->delayed_welcome = FALSE; - ls->connections = g_list_prepend(ls->connections, lc); - ls->n_connections++; - - dbus_connection_setup_with_g_main(c, NULL); - - b = dbus_connection_add_filter(c, message_function, lc, NULL); - g_assert(b); - - m = dbus_message_new_signal("/", LASSI_INTERFACE, "Hello"); - g_assert(m); - - ag = ls->active_generation; - og = ls->order_generation; - cg = ls->clipboard_generation; - - b = dbus_message_append_args( - m, - DBUS_TYPE_STRING, &ls->id, - DBUS_TYPE_STRING, &ls->address, - DBUS_TYPE_INT32, &ag, - DBUS_TYPE_INT32, &og, - DBUS_TYPE_INT32, &cg, - DBUS_TYPE_INVALID); - g_assert(b); - - fd = -1; - dbus_connection_get_socket(c, &fd); - g_assert(fd >= 0); - - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) - g_warning("Failed to enable TCP_NODELAY"); - - b = dbus_connection_send(c, m, NULL); - g_assert(b); - - dbus_message_unref(m); - - lassi_tray_update(&ls->tray_info, ls->n_connections); - return lc; -} - -static void new_connection(DBusServer *s, DBusConnection *c, void *userdata) { - LassiServer *ls = userdata; - - g_assert(s); - g_assert(c); - - if (ls->n_connections >= CONNECTIONS_MAX) - return; - - dbus_connection_set_allow_anonymous(c, TRUE); - connection_add(ls, c, FALSE); -} - -static int server_init(LassiServer *ls) { - DBusError e; - int r = -1; - guint16 port; - - g_assert(ls); - - memset(ls, 0, sizeof(*ls)); - - dbus_error_init(&e); - - for (port = PORT_MIN; port < PORT_MAX; port++) { - char *t; - - t = g_strdup_printf("tcp:port=%u,host=0.0.0.0", port); - ls->dbus_server = dbus_server_listen(t, &e); - g_free(t); - - if (ls->dbus_server) { - ls->port = port; - break; - } - - if (!dbus_error_has_name(&e, DBUS_ERROR_ADDRESS_IN_USE)) { - g_warning("Failed to create D-Bus server: %s %s", e.message, e.name); - goto finish; - } - - dbus_error_free(&e); - } - - if (!ls->dbus_server) { - g_warning("All ports blocked."); - goto finish; - } - - g_debug("Listening on port %u", port); - - dbus_server_setup_with_g_main(ls->dbus_server, NULL); - dbus_server_set_new_connection_function(ls->dbus_server, new_connection, ls, NULL); - - ls->connections_by_id = g_hash_table_new(g_str_hash, g_str_equal); - - ls->id = g_strdup_printf("%s's desktop on %s", g_get_user_name(), g_get_host_name()); - - if (lassi_avahi_init(&ls->avahi_info, ls) < 0) - goto finish; - - /* The initialization of Avahi might have changed ls->id! */ - - ls->address = dbus_server_get_address(ls->dbus_server); - ls->order = g_list_prepend(NULL, g_strdup(ls->id)); - - if (lassi_grab_init(&ls->grab_info, ls) < 0) - goto finish; - - if (lassi_osd_init(&ls->osd_info) < 0) - goto finish; - - if (lassi_clipboard_init(&ls->clipboard_info, ls) < 0) - goto finish; - - - if (lassi_tray_init(&ls->tray_info, ls) < 0) - goto finish; - - if (lassi_prefs_init(&ls->prefs_info, ls) < 0) - goto finish; - - r = 0; - -finish: - dbus_error_free(&e); - return r; -} - -void lassi_server_disconnect(LassiServer *ls, const char *id, gboolean remove_from_order) { - LassiConnection *lc; - - g_assert(ls); - g_assert(id); - - if ((lc = g_hash_table_lookup(ls->connections_by_id, id))) - connection_unlink(lc, remove_from_order); - else if (remove_from_order) { - GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); - - if (i) - ls->order = g_list_delete_link(ls->order, i); - } -} - -static void server_disconnect_all(LassiServer *ls, gboolean clear_order) { - - while (ls->connections) - connection_unlink(ls->connections->data, clear_order); - - if (clear_order) { - lassi_list_free(ls->order); - ls->order = NULL; - } -} - -static void server_done(LassiServer *ls) { - - g_assert(ls); - - if (ls->dbus_server) { - dbus_server_disconnect(ls->dbus_server); - dbus_server_unref(ls->dbus_server); - } - - server_disconnect_all(ls, FALSE); - - if (ls->connections_by_id) - g_hash_table_destroy(ls->connections_by_id); - - g_free(ls->id); - g_free(ls->address); - - lassi_list_free(ls->order); - - lassi_grab_done(&ls->grab_info); - lassi_osd_done(&ls->osd_info); - lassi_clipboard_done(&ls->clipboard_info); - lassi_avahi_done(&ls->avahi_info); - lassi_tray_done(&ls->tray_info); - lassi_prefs_done(&ls->prefs_info); - - memset(ls, 0, sizeof(*ls)); -} - -gboolean lassi_server_is_connected(LassiServer *ls, const char *id) { - g_assert(ls); - g_assert(id); - - return strcmp(id, ls->id) == 0 || g_hash_table_lookup(ls->connections_by_id, id); -} - -gboolean lassi_server_is_known(LassiServer *ls, const char *id) { - g_assert(ls); - g_assert(id); - - return !!g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); -} - -LassiConnection* lassi_server_connect(LassiServer *ls, const char *a) { - DBusError e; - DBusConnection *c; - LassiConnection *lc = NULL; - - dbus_error_init(&e); - - if (ls->n_connections >= CONNECTIONS_MAX) - goto finish; - - if (!(c = dbus_connection_open_private(a, &e))) { - g_warning("Failed to connect to client: %s", e.message); - goto finish; - } - - lc = connection_add(ls, c, TRUE); - -finish: - - if (c) - dbus_connection_unref(c); - - dbus_error_free(&e); - return lc; -} - -int main(int argc, char *argv[]) { - LassiServer ls; - - gtk_init(&argc, &argv); - - memset(&ls, 0, sizeof(ls)); - - if (server_init(&ls) < 0) - goto fail; - - gtk_main(); - -fail: - - server_done(&ls); - - return 0; -} diff --git a/lassi-server.h b/lassi-server.h deleted file mode 100644 index 55849d4..0000000 --- a/lassi-server.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef foolassiserverhfoo -#define foolassiserverhfoo - -#include -#include - -typedef struct LassiServer LassiServer; -typedef struct LassiConnection LassiConnection; - -#include "lassi-grab.h" -#include "lassi-osd.h" -#include "lassi-clipboard.h" -#include "lassi-avahi.h" -#include "lassi-tray.h" -#include "lassi-prefs.h" - -struct LassiServer { - DBusServer *dbus_server; - - char *id, *address; - uint16_t port; - - /* All connections */ - GList *connections; - int n_connections; - - /* Configured connections */ - GHashTable *connections_by_id; - GList *connections_left, *connections_right; /* stored from right to left, resp, left to right */ - - /* Active display management */ - int active_generation; - LassiConnection *active_connection; - - /* Layout management */ - int order_generation; - GList *order; - - /* Clipboard CLIPBOARD management */ - int clipboard_generation; - LassiConnection *clipboard_connection; - gboolean clipboard_empty; - - /* Clipboard PRIMARY management */ - int primary_generation; - LassiConnection *primary_connection; - gboolean primary_empty; - - LassiGrabInfo grab_info; - LassiOsdInfo osd_info; - LassiClipboardInfo clipboard_info; - LassiAvahiInfo avahi_info; - LassiTrayInfo tray_info; - LassiPrefsInfo prefs_info; -}; - -struct LassiConnection { - LassiServer *server; - - DBusConnection *dbus_connection; - char *id, *address; - - gboolean we_are_client; - gboolean delayed_welcome; -}; - -int lassi_server_change_grab(LassiServer *s, gboolean to_left, int y); -int lassi_server_acquire_grab(LassiServer *s); - -int lassi_server_motion_event(LassiServer *s, int dx, int dy); -int lassi_server_button_event(LassiServer *ls, unsigned button, gboolean is_press); -int lassi_server_key_event(LassiServer *ls, unsigned key, gboolean is_press); - -int lassi_server_acquire_clipboard(LassiServer *ls, gboolean primary, char**targets); -int lassi_server_return_clipboard(LassiServer *ls, gboolean primary); -int lassi_server_get_clipboard(LassiServer *ls, gboolean primary, const char *t, int *f, gpointer *p, int *l); - -LassiConnection* lassi_server_connect(LassiServer *ls, const char *a); -void lassi_server_disconnect(LassiServer *ls, const char *id, gboolean remove_from_order); - -gboolean lassi_server_is_connected(LassiServer *ls, const char *id); -gboolean lassi_server_is_known(LassiServer *ls, const char *id); - -#endif diff --git a/lassi-tray.c b/lassi-tray.c deleted file mode 100644 index 72fc310..0000000 --- a/lassi-tray.c +++ /dev/null @@ -1,100 +0,0 @@ -#include - -#include - -#include - -#include "lassi-tray.h" -#include "lassi-server.h" - -#define ICON_IDLE "network-wired" -#define ICON_BUSY "network-workgroup" - -static void on_prefs_activate(GtkMenuItem *menuitem, LassiTrayInfo *i) { - lassi_prefs_show(&i->server->prefs_info); -} - -static void on_tray_activate(GtkStatusIcon *status_icon, LassiTrayInfo *i) { - gtk_menu_popup(GTK_MENU(i->menu), NULL, NULL, gtk_status_icon_position_menu, i->status_icon, 0, gtk_get_current_event_time()); -} - -static void on_tray_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, LassiTrayInfo *i) { - on_tray_activate(status_icon, i); -} - -int lassi_tray_init(LassiTrayInfo *i, LassiServer *server) { - GtkWidget *item; - g_assert(i); - g_assert(server); - - memset(i, 0, sizeof(*i)); - i->server = server; - - notify_init("Mango Lassi"); - - i->status_icon = gtk_status_icon_new_from_icon_name(ICON_IDLE); - - i->menu = gtk_menu_new(); - item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL); - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(on_prefs_activate), i); - gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); - item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_main_quit), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); - gtk_widget_show_all(i->menu); - - g_signal_connect(G_OBJECT(i->status_icon), "popup_menu", G_CALLBACK(on_tray_popup_menu), i); - g_signal_connect(G_OBJECT(i->status_icon), "activate", G_CALLBACK(on_tray_activate), i); - - lassi_tray_update(i, 0); - - return 0; -} - -void lassi_tray_update(LassiTrayInfo *i, int n_connected) { - char *t; - g_assert(i); - - gtk_status_icon_set_from_icon_name(i->status_icon, n_connected > 0 ? ICON_BUSY : ICON_IDLE); - - if (n_connected == 0) - t = g_strdup("No desktops connected."); - else if (n_connected == 1) - t = g_strdup("1 desktop connected."); - else - t = g_strdup_printf("%i desktops connected.", n_connected); - - gtk_status_icon_set_tooltip(i->status_icon, t); - - g_free(t); -} - -void lassi_tray_show_notification(LassiTrayInfo *i, char *summary, char *body, LassiTrayNotificationIcon icon) { - - static const char * const icon_name[] = { - [LASSI_TRAY_NOTIFICATION_WELCOME] = "user-desktop", - [LASSI_TRAY_NOTIFICATION_LEFT] = "go-previous", - [LASSI_TRAY_NOTIFICATION_RIGHT] = "go-next" - }; - - NotifyNotification *n; - - n = notify_notification_new_with_status_icon(summary, body, icon_name[icon], i->status_icon); - notify_notification_set_timeout(n, 10000); - notify_notification_set_urgency(n, NOTIFY_URGENCY_LOW); - notify_notification_set_category(n, "network"); - notify_notification_show(n, NULL); - -} - -void lassi_tray_done(LassiTrayInfo *i) { - g_assert(i); - - g_object_unref(G_OBJECT(i->status_icon)); - - notify_uninit(); - - memset(i, 0, sizeof(*i)); -} diff --git a/lassi-tray.h b/lassi-tray.h deleted file mode 100644 index d316092..0000000 --- a/lassi-tray.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef foolassitrayhfoo -#define foolassitrayhfoo - -#include -#include - -typedef struct LassiTrayInfo LassiTrayInfo; -struct LassiServer; - -typedef enum LassiTrayNotificationIcon { - LASSI_TRAY_NOTIFICATION_WELCOME, - LASSI_TRAY_NOTIFICATION_LEFT, - LASSI_TRAY_NOTIFICATION_RIGHT -} LassiTrayNotificationIcon; - -struct LassiTrayInfo { - struct LassiServer *server; - - GtkStatusIcon *status_icon; - GtkWidget *menu; -}; - -#include "lassi-server.h" - -int lassi_tray_init(LassiTrayInfo *i, LassiServer *server); -void lassi_tray_done(LassiTrayInfo *i); -void lassi_tray_update(LassiTrayInfo *i, int n_connected); - -void lassi_tray_show_notification(LassiTrayInfo *i, char *summary, char *body, LassiTrayNotificationIcon icon); - - -#endif diff --git a/mango-lassi.glade b/mango-lassi.glade deleted file mode 100644 index 2be70a0..0000000 --- a/mango-lassi.glade +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 8 - Input Sharing - GTK_WIN_POS_CENTER_ON_PARENT - user-desktop - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 12 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 4 - 12 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - False - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 6 - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 6 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-add - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Add - True - - - GTK_PACK_END - 1 - - - - - - - False - - - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 6 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-go-up - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Up - True - - - GTK_PACK_END - 1 - - - - - - - False - 1 - - - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-go-down - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Down - True - - - GTK_PACK_END - 1 - - - - - - - False - 2 - - - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 6 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-remove - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Remove - True - - - False - GTK_PACK_END - 1 - - - - - - - False - 3 - - - - - False - 1 - - - - - 2 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 4 - 6 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 - gtk-dialog-info - 6 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - <i>Reorder the desktops you're already sharing mouse and keyboard with, or add new desktops to your session. -Order the desktops in the list above from left to right how they are positioned on your desk. -Please make sure to run Mango Lassi Input Sharing on all computers you want so share input with.</i> - True - GTK_JUSTIFY_CENTER - True - True - 50 - - - 1 - - - - - False - GTK_PACK_END - 1 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - GTK_BUTTONBOX_END - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-close - True - 0 - - - - - - False - GTK_PACK_END - - - - - - diff --git a/src/lassi-avahi.c b/src/lassi-avahi.c new file mode 100644 index 0000000..258c199 --- /dev/null +++ b/src/lassi-avahi.c @@ -0,0 +1,255 @@ +#include + +#include +#include +#include +#include + +#include "lassi-avahi.h" + +/* FIXME: Error and collision handling is suboptimal */ + +static void resolve_cb( + AvahiServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void* userdata) { + + LassiAvahiInfo *i = userdata; + + g_assert(r); + g_assert(i); + + /* Called whenever a service has been resolved successfully or timed out */ + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX], *t; + + avahi_address_snprint(a, sizeof(a), address); + t = g_strdup_printf("tcp:port=%u,host=%s", port, a); + lassi_server_connect(i->server, t); + g_free(t); + break; + } + + case AVAHI_RESOLVER_FAILURE: + g_message("Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror(avahi_client_errno(i->client))); + break; + + } + + avahi_service_resolver_free(r); + } + +static void browse_cb( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { + + LassiAvahiInfo *i = userdata; + + g_assert(b); + g_assert(i); + + switch (event) { + case AVAHI_BROWSER_NEW: + + if (!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN) && + !lassi_server_is_connected(i->server, name) && + lassi_server_is_known(i->server, name)) + + if (!(avahi_service_resolver_new(i->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_cb, i))) + g_message("Failed to resolve service '%s': %s", name, avahi_strerror(avahi_client_errno(i->client))); + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + + case AVAHI_BROWSER_FAILURE: + g_message("Browsing failed: %s", avahi_strerror(avahi_client_errno(i->client))); + gtk_main_quit(); + break; + } +} + + +static void create_service(LassiAvahiInfo *i); + +static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + LassiAvahiInfo *i = userdata; + + g_assert(g); + g_assert(i); + + i->group = g; + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + g_message("Service '%s' successfully established.", i->service_name); + break; + + case AVAHI_ENTRY_GROUP_COLLISION : { + char *n; + + n = avahi_alternative_service_name(i->service_name); + avahi_free(i->service_name); + i->service_name = n; + + g_message("Service name collision, renaming service to '%s'", n); + + /* And recreate the services */ + create_service(i); + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + g_message("Entry group failure: %s", avahi_strerror(avahi_client_errno(i->client))); + gtk_main_quit(); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + +static void create_service(LassiAvahiInfo *i) { + g_assert(i); + + if (!i->group) + if (!(i->group = avahi_entry_group_new(i->client, entry_group_callback, i))) { + g_message("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(i->client))); + gtk_main_quit(); + return; + } + + if (avahi_entry_group_is_empty(i->group)) { + int ret; + + for (;;) { + + if (!i->service_name) + i->service_name = g_strdup(i->server->id); + + if ((ret = avahi_entry_group_add_service(i->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, i->service_name, LASSI_SERVICE_TYPE, NULL, NULL, i->server->port, NULL)) < 0) { + + if (ret == AVAHI_ERR_COLLISION) { + char *n = avahi_alternative_service_name(i->service_name); + avahi_free(i->service_name); + i->service_name = n; + continue; + } + + g_message("Failed to add service: %s", avahi_strerror(ret)); + gtk_main_quit(); + return; + } + + break; + } + + if (strcmp(i->service_name, i->server->id)) { + g_free(i->server->id); + i->server->id = g_strdup(i->service_name); + } + + if ((ret = avahi_entry_group_commit(i->group)) < 0) { + g_message("Failed to commit entry group: %s", avahi_strerror(ret)); + gtk_main_quit(); + return; + } + } +} + +static void client_cb(AvahiClient *client, AvahiClientState state, void *userdata) { + LassiAvahiInfo *i = userdata; + + i->client = client; + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + if (!i->group) + create_service(i); + break; + + case AVAHI_CLIENT_FAILURE: + g_message("Client failure: %s", avahi_strerror(avahi_client_errno(client))); + gtk_main_quit(); + break; + + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + if (i->group) + avahi_entry_group_reset(i->group); + + break; + + case AVAHI_CLIENT_CONNECTING: + ; + } +} + +int lassi_avahi_init(LassiAvahiInfo *i, LassiServer *server) { + int error; + + g_assert(i); + g_assert(server); + + memset(i, 0, sizeof(*i)); + i->server = server; + + avahi_set_allocator(avahi_glib_allocator()); + + if (!(i->poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) { + g_message("avahi_glib_poll_new() failed."); + goto fail; + } + + if (!(i->client = avahi_client_new(avahi_glib_poll_get(i->poll), 0, client_cb, i, &error))) { + g_message("avahi_client_new() failed: %s", avahi_strerror(error)); + goto fail; + } + + if (!(i->browser = avahi_service_browser_new(i->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LASSI_SERVICE_TYPE, NULL, 0, browse_cb, i))) { + g_message("avahi_service_browser_new(): %s", avahi_strerror(avahi_client_errno(i->client))); + goto fail; + } + + return 0; + +fail: + lassi_avahi_done(i); + return -1; +} + +void lassi_avahi_done(LassiAvahiInfo *i) { + g_assert(i); + + if (i->client) + avahi_client_free(i->client); + + if (i->poll) + avahi_glib_poll_free(i->poll); + + g_free(i->service_name); + + memset(i, 0, sizeof(*i)); +} diff --git a/src/lassi-avahi.h b/src/lassi-avahi.h new file mode 100644 index 0000000..04b634c --- /dev/null +++ b/src/lassi-avahi.h @@ -0,0 +1,31 @@ +#ifndef foolassiavahihfoo +#define foolassiavahihfoo + +#include +#include +#include +#include + +typedef struct LassiAvahiInfo LassiAvahiInfo; +struct LassiServer; + +struct LassiAvahiInfo { + struct LassiServer *server; + + AvahiGLibPoll *poll; + AvahiClient *client; + + AvahiEntryGroup *group; + char *service_name; + + AvahiServiceBrowser *browser; +}; + +#include "lassi-server.h" + +int lassi_avahi_init(LassiAvahiInfo *i, LassiServer *server); +void lassi_avahi_done(LassiAvahiInfo *i); + +#define LASSI_SERVICE_TYPE "_mango-lassi._tcp" + +#endif diff --git a/src/lassi-clipboard.c b/src/lassi-clipboard.c new file mode 100644 index 0000000..bdfeff4 --- /dev/null +++ b/src/lassi-clipboard.c @@ -0,0 +1,174 @@ +#include +#include + +#include "lassi-server.h" + +#define LASSI_MARKER "application/x-mango-lassi-marker" + +static void targets_received(GtkClipboard *clipboard, GdkAtom *atoms, int n_atoms, gpointer userdata) { + int j, k; + LassiClipboardInfo *i = userdata; + char **targets; + + g_assert(clipboard); + g_assert(i); + + g_debug("recvd targs %p, %i", atoms, n_atoms); + + if (!atoms) + return; + + targets = g_new0(char*, n_atoms+1); + + for (j = 0, k = 0; j < n_atoms; j++) { + char *c = gdk_atom_name(atoms[j]); + + /* Avoid loops */ + if (strcmp(c, LASSI_MARKER) == 0) { + g_free(c); + goto fail; + } + + if (strcmp(c, "TIMESTAMP") == 0 || + strcmp(c, "TARGETS") == 0 || + strcmp(c, "CLIPBOARD_MANAGER") == 0 || + strcmp(c, "CLIENT_WINDOW") == 0 || + strcmp(c, "DELETE") == 0 || + strcmp(c, "INSERT_PROPERTY") == 0 || + strcmp(c, "INSERT_SELECTION") == 0 || + strcmp(c, "LENGTH") == 0 || + strcmp(c, "TASK") == 0 || + strcmp(c, "MULTIPLE") == 0 || + strcmp(c, "DRAWABLE") == 0) { + g_free(c); + continue; + } + + targets[k++] = c; + } + + g_debug("%p %i", targets, n_atoms); + lassi_server_acquire_clipboard(i->server, clipboard == i->primary, targets); + +fail: + g_strfreev(targets); +} + +static void owner_change(GtkClipboard *clipboard, GdkEventOwnerChange *event, gpointer userdata) { + LassiClipboardInfo *i = userdata; + + g_assert(clipboard); + g_assert(i); + + g_debug("owner change"); + + if (event->reason == GDK_OWNER_CHANGE_NEW_OWNER) + gtk_clipboard_request_targets(clipboard, targets_received, i); + else + lassi_server_return_clipboard(i->server, clipboard == i->primary); +} + +static void get_func(GtkClipboard *clipboard, GtkSelectionData *sd, guint info, gpointer userdata) { + LassiClipboardInfo *i = userdata; + char *t; + int f = 0; + gpointer d = NULL; + gint l = 0; + + g_assert(clipboard); + g_assert(i); + + t = gdk_atom_name(sd->target); + + g_debug("get(%s)", t); + + if (lassi_server_get_clipboard(i->server, clipboard == i->primary, t, &f, &d, &l) >= 0) { + g_debug("successfully got data"); + gtk_selection_data_set(sd, sd->target, f, d, l); + } else + g_debug("failed to get data"); + + g_free(d); + g_free(t); +} + +static void clear_func(GtkClipboard *clipboard, gpointer userdata) { + LassiClipboardInfo *i = userdata; + + g_assert(clipboard); + g_assert(i); + + g_debug("clear"); + + gtk_clipboard_request_targets(clipboard, targets_received, i); +} + +int lassi_clipboard_init(LassiClipboardInfo *i, LassiServer *s) { + g_assert(i); + g_assert(s); + + memset(i, 0, sizeof(*i)); + i->server = s; + + i->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + i->primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + + g_signal_connect(i->clipboard, "owner_change", G_CALLBACK(owner_change), i); + g_signal_connect(i->primary, "owner_change", G_CALLBACK(owner_change), i); + return 0; +} + +void lassi_clipboard_done(LassiClipboardInfo *i) { + g_assert(i); + + memset(i, 0, sizeof(*i)); +} + +void lassi_clipboard_set(LassiClipboardInfo *i, gboolean primary, char *targets[]) { + int n = 0, j; + gboolean b; + char **t; + GtkTargetEntry *e; + + for (t = targets; *t; t++) + n++; + + e = g_new0(GtkTargetEntry, n+1); + + for (t = targets, j = 0; *t; t++, j++) { + e[j].target = *t; + e[j].info = j; + } + + e[j].target = LASSI_MARKER; + e[j].info = j; + + g_debug("setting %i targets", n+1); + + b = gtk_clipboard_set_with_data(primary ? i->primary : i->clipboard, e, n+1, get_func, clear_func, i); + g_assert(b); +} + +void lassi_clipboard_clear(LassiClipboardInfo *i, gboolean primary) { + g_assert(i); + + gtk_clipboard_clear(primary ? i->primary : i->clipboard); +} + +int lassi_clipboard_get(LassiClipboardInfo *i, gboolean primary, const char *target, int *f, gpointer *p, int *l) { + GtkSelectionData*sd; + g_assert(i); + + if (!(sd = gtk_clipboard_wait_for_contents(primary ? i->primary : i->clipboard, gdk_atom_intern(target, TRUE)))) + return -1; + + g_assert(sd->length > 0); + + *f = sd->format; + *p = g_memdup(sd->data, sd->length); + *l = sd->length; + + gtk_selection_data_free(sd); + + return 0; +} diff --git a/src/lassi-clipboard.h b/src/lassi-clipboard.h new file mode 100644 index 0000000..f5574e3 --- /dev/null +++ b/src/lassi-clipboard.h @@ -0,0 +1,24 @@ +#ifndef foolassiclipboardhfoo +#define foolassiclipboardhfoo + +#include + +typedef struct LassiClipboardInfo LassiClipboardInfo; +struct LassiServer; + +struct LassiClipboardInfo { + struct LassiServer *server; + + GtkClipboard *clipboard, *primary; +}; + +#include "lassi-server.h" + +int lassi_clipboard_init(LassiClipboardInfo *i, LassiServer *server); +void lassi_clipboard_done(LassiClipboardInfo *i); + +void lassi_clipboard_set(LassiClipboardInfo *i, gboolean primary, char *targets[]); +void lassi_clipboard_clear(LassiClipboardInfo *i, gboolean primary); +int lassi_clipboard_get(LassiClipboardInfo *i, gboolean primary, const char *target, int *format, gpointer *p, int *l); + +#endif diff --git a/src/lassi-grab.c b/src/lassi-grab.c new file mode 100644 index 0000000..e2bda77 --- /dev/null +++ b/src/lassi-grab.c @@ -0,0 +1,407 @@ + +#include +#include + +#include +#include + +#include +#include + +#include "lassi-server.h" +#include "lassi-grab.h" + +#define TRIGGER_WIDTH 1 + +static int local2global(LassiGrabInfo *i, int y) { + g_assert(i); + g_assert(y >= 0 && y <= gdk_screen_get_height(i->screen)-1); + + /* Convert local screen coordinates (0 .. height) into global ones (0 . 65535) */ + return (y * 0xFFFF) / (gdk_screen_get_height(i->screen)-1); +} + +static int global2local(LassiGrabInfo *i, int y) { + g_assert(i); + g_assert(y >= 0 && y <= 0xFFFF); + + /* Convert global screen coordinates (0 . 65535) into local ones (0 .. height) */ + return (y * (gdk_screen_get_height(i->screen)-1)) / 0xFFFF; +} + +static void move_pointer(LassiGrabInfo *i, int x, int y) { + g_assert(i); + + /* Move the pointer ... */ + gdk_display_warp_pointer(i->display, i->screen, x, y); + + i->last_x = x; + i->last_y = y; +} + +static void drop_motion_events(LassiGrabInfo *i) { + XEvent txe; + + g_assert(i); + + /* Drop all queued motion events */ + while (XCheckTypedEvent(GDK_DISPLAY_XDISPLAY(i->display), MotionNotify, &txe)) + ; +} + +static int grab_input(LassiGrabInfo *i, GdkWindow *w) { + g_assert(i); + g_assert(w); + + if (gdk_pointer_grab(w, TRUE, + GDK_POINTER_MOTION_MASK| + GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, + NULL, i->empty_cursor, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) { + g_debug("pointer grab failed"); + return -1; + } + + + if (gdk_keyboard_grab(w, TRUE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) { + gdk_pointer_ungrab(GDK_CURRENT_TIME); + g_debug("keyboard grab failed"); + return -1; + } + + XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), False); + + if (i->grab_window != w) { + /* Now, rebase the pointer, so that we can easily calculate + * relative movements */ + move_pointer(i, i->base_x, i->base_y); + + i->grab_window = w; + + i->left_shift = i->right_shift = i->double_shift = FALSE; + + g_debug("Input now grabbed"); + } + + return 0; +} + +int lassi_grab_start(LassiGrabInfo *i, gboolean to_left) { + g_assert(i); + + return grab_input(i, to_left ? i->left_window : i->right_window); +} + +void lassi_grab_stop(LassiGrabInfo *i, int y) { + int x; + + g_assert(i); + + if (!i->grab_window) + return; + + /* Move the pointer back into our screen */ + if (y >= 0 && y < 0xFFFF) { + + /* We received a valid y coordinate, so let's use it */ + y = global2local(i, y); + + if (i->grab_window == i->left_window) + x = TRIGGER_WIDTH; + else + x = gdk_screen_get_width(i->screen)-TRIGGER_WIDTH-1; + + } else { + + /* We received an invlid y coordinate, so let's center the + * pointer */ + x = i->base_x; + y = i->base_y; + } + + move_pointer(i, x, y); + + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_pointer_ungrab(GDK_CURRENT_TIME); + + drop_motion_events(i); + + i->grab_window = NULL; + + g_debug("Input now ungrabbed"); + + XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), True); +} + +static void handle_motion(LassiGrabInfo *i, int x, int y) { + int dx, dy; + int r; + int w, h; + + dx = x - i->last_x; + dy = y - i->last_y; + + i->last_x = x; + i->last_y = y; + +/* g_debug("rel motion %i %i", dx, dy); */ + + w = gdk_screen_get_width(i->screen); + h = gdk_screen_get_height(i->screen); + + if (x <= w/10 || y <= h/10 || + x >= (w*9)/10 || y >= (h*9)/10) { + + XEvent txe; + + /* Pointer is too near to the edges, move cursor + * back to center, so that further movements are + * not clipped */ + + g_debug("centering"); + + /* First, make sure there is no further motion event in the queue */ + while (XCheckTypedEvent(GDK_DISPLAY_XDISPLAY(i->display), MotionNotify, &txe)) { + dx += txe.xmotion.x - i->last_x; + dy += txe.xmotion.y - i->last_y; + + i->last_x = txe.xmotion.x; + i->last_y = txe.xmotion.y; + } + + move_pointer(i, i->base_x, i->base_y); + } + + /* Filter out non-existant or too large motions */ + if ((dx != 0 || dy != 0) && + ((abs(dx) <= (w*9)/20) && (abs(dy) <= (h*9)/20))) { + +/* g_debug("sending motion"); */ + + /* Send the event */ + r = lassi_server_motion_event(i->server, dx, dy); + g_assert(r >= 0); + } +} + +static GdkFilterReturn filter_func(GdkXEvent *gxe, GdkEvent *event, gpointer data) { + LassiGrabInfo *i = data; + XEvent *xe = (XEvent*) gxe; + GdkWindow *w = ((GdkEventAny*) event)->window; + + g_assert(i); + g_assert(xe); + g_assert(event); + + switch (xe->type){ + + case EnterNotify: { + XEnterWindowEvent *ewe = (XEnterWindowEvent*) xe; + + if (ewe->mode == NotifyNormal && ewe->state == 0 && !i->grab_window) { +/* g_debug("enter %u %u", ewe->x_root, ewe->y_root); */ + + /* Only honour this when no button/key is pressed */ + + if (lassi_server_change_grab(i->server, w == i->left_window, local2global(i, ewe->y_root)) >= 0) + grab_input(i, w); + + } else if (i->grab_window) + handle_motion(i, ewe->x_root, ewe->y_root); + + break; + } + + case MotionNotify: + + if (i->grab_window) { + XMotionEvent *me = (XMotionEvent*) xe; + +/* g_debug("motion %u %u", me->x_root, me->y_root); */ + handle_motion(i, me->x_root, me->y_root); + } + + break; + + case ButtonPress: + case ButtonRelease: + + if (i->grab_window) { + int r; + XButtonEvent *be = (XButtonEvent*) xe; + +/* g_debug("button press/release"); */ + handle_motion(i, be->x_root, be->y_root); + + /* Send the event */ + r = lassi_server_button_event(i->server, be->button, xe->type == ButtonPress); + g_assert(r >= 0); + } + break; + + case KeyPress: + case KeyRelease: + +/* g_debug("raw key"); */ + + if (i->grab_window) { + int r; + XKeyEvent *ke = (XKeyEvent *) xe; + KeySym keysym; + + keysym = XKeycodeToKeysym(GDK_DISPLAY_XDISPLAY(i->display), ke->keycode, 0); + + if (keysym == XK_Shift_L) + i->left_shift = ke->type == KeyPress; + if (keysym == XK_Shift_R) + i->right_shift = xe->type == KeyPress; + + if (i->left_shift && i->right_shift) + i->double_shift = TRUE; + +/* g_debug("left_shift=%i right_shift=%i 0x04%x", i->left_shift, i->right_shift, (unsigned) keysym); */ + +/* g_debug("key press/release"); */ + handle_motion(i, ke->x_root, ke->y_root); + + /* Send the event */ + r = lassi_server_key_event(i->server, keysym, xe->type == KeyPress); + g_assert(r >= 0); + + if (!i->left_shift && !i->right_shift && i->double_shift) { +/* g_debug("Got double shift"); */ + lassi_server_acquire_grab(i->server); + lassi_grab_stop(i, -1); + } + } + break; + } + + return GDK_FILTER_CONTINUE; +} + +int lassi_grab_init(LassiGrabInfo *i, LassiServer *s) { + GdkWindowAttr wa; + GdkColor black = { 0, 0, 0, 0 }; + const gchar cursor_data[1] = { 0 }; + GdkBitmap *bitmap; + int xtest_event_base, xtest_error_base; + int major_version, minor_version; + + memset(i, 0, sizeof(*i)); + i->server = s; + + i->screen = gdk_screen_get_default(); + i->display = gdk_screen_get_display(i->screen); + i->root = gdk_screen_get_root_window(i->screen); + + if (!XTestQueryExtension(GDK_DISPLAY_XDISPLAY(i->display), &xtest_event_base, &xtest_error_base, &major_version, &minor_version)) { + g_warning("XTest extension not supported."); + return -1; + } + + g_debug("XTest %u.%u supported.", major_version, minor_version); + + /* Create empty cursor */ + bitmap = gdk_bitmap_create_from_data(NULL, cursor_data, 1, 1); + i->empty_cursor = gdk_cursor_new_from_pixmap(bitmap, bitmap, &black, &black, 0, 0); + gdk_pixmap_unref(bitmap); + + /* Create trigger windows */ + memset(&wa, 0, sizeof(wa)); + + wa.title = "Mango Lassi Left"; + wa.event_mask = GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK; + wa.x = 0; + wa.y = gdk_screen_get_height(i->screen)/20; + wa.width = TRIGGER_WIDTH; + wa.height = (gdk_screen_get_height(i->screen)*18)/20; + wa.wclass = GDK_INPUT_ONLY; + wa.window_type = GDK_WINDOW_FOREIGN; + wa.override_redirect = TRUE; + wa.type_hint = GDK_WINDOW_TYPE_HINT_DOCK; + wa.cursor = i->empty_cursor; + + i->left_window = gdk_window_new(i->root, &wa, GDK_WA_TITLE|GDK_WA_X|GDK_WA_Y|GDK_WA_NOREDIR|GDK_WA_TYPE_HINT|GDK_WA_CURSOR); + gdk_window_set_keep_above(i->left_window, TRUE); + gdk_window_add_filter(i->left_window, filter_func, i); + + wa.title = "Mango Lassi Right"; + wa.x = gdk_screen_get_width(i->screen) - TRIGGER_WIDTH; + + i->right_window = gdk_window_new(i->root, &wa, GDK_WA_TITLE|GDK_WA_X|GDK_WA_Y|GDK_WA_NOREDIR|GDK_WA_TYPE_HINT); + gdk_window_set_keep_above(i->right_window, TRUE); + gdk_window_add_filter(i->right_window, filter_func, i); + + i->base_x = gdk_screen_get_width(i->screen)/2; + i->base_y = gdk_screen_get_height(i->screen)/2; + + XTestGrabControl(GDK_DISPLAY_XDISPLAY(i->display), True); + + return 0; +} + +void lassi_grab_done(LassiGrabInfo *i) { + g_assert(i); + + lassi_grab_stop(i, -1); + + if (i->left_window) + gdk_window_destroy(i->left_window); + + if (i->right_window) + gdk_window_destroy(i->right_window); + + if (i->empty_cursor) + gdk_cursor_unref(i->empty_cursor); +} + +void lassi_grab_enable_triggers(LassiGrabInfo *i, gboolean left, gboolean right) { + g_assert(i); + + g_debug("Showing windows: left=%s, right=%s", left ? "yes" : "no", right ? "yes" : "no"); + + if (left) + gdk_window_show(i->left_window); + else + gdk_window_hide(i->left_window); + + if (right) + gdk_window_show(i->right_window); + else + gdk_window_hide(i->right_window); +} + +int lassi_grab_move_pointer_relative(LassiGrabInfo *i, int dx, int dy) { + g_assert(i); + + if (i->grab_window) + return -1; + + XTestFakeRelativeMotionEvent(GDK_DISPLAY_XDISPLAY(i->display), dx, dy, 0); + XSync(GDK_DISPLAY_XDISPLAY(i->display), False); + + return 0; +} + +int lassi_grab_press_button(LassiGrabInfo *i, unsigned button, gboolean is_press) { + g_assert(i); + + if (i->grab_window) + return -1; + + XTestFakeButtonEvent(GDK_DISPLAY_XDISPLAY(i->display), button, is_press, 0); + XSync(GDK_DISPLAY_XDISPLAY(i->display), False); + + return 0; +} + +int lassi_grab_press_key(LassiGrabInfo *i, unsigned key, gboolean is_press) { + g_assert(i); + if (i->grab_window) + return -1; + + XTestFakeKeyEvent(GDK_DISPLAY_XDISPLAY(i->display), XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(i->display), key), is_press, 0); + XSync(GDK_DISPLAY_XDISPLAY(i->display), False); + + return 0; +} diff --git a/src/lassi-grab.h b/src/lassi-grab.h new file mode 100644 index 0000000..a9366e0 --- /dev/null +++ b/src/lassi-grab.h @@ -0,0 +1,40 @@ +#ifndef foolassigrabhfoo +#define foolassigrabhfoo + +#include + +typedef struct LassiGrabInfo LassiGrabInfo; +struct LassiServer; + +struct LassiGrabInfo { + struct LassiServer *server; + + GdkDisplay *display; + GdkScreen *screen; + GdkWindow *root; + + GdkWindow *left_window, *right_window; + GdkCursor *empty_cursor; + GdkWindow *grab_window; + + int base_x, base_y; + int last_x, last_y; + + gboolean left_shift, right_shift, double_shift; +}; + +#include "lassi-server.h" + +int lassi_grab_init(LassiGrabInfo *i, LassiServer *server); +void lassi_grab_done(LassiGrabInfo *i); + +int lassi_grab_start(LassiGrabInfo *i, gboolean to_left); +void lassi_grab_stop(LassiGrabInfo *i, int y); + +void lassi_grab_enable_triggers(LassiGrabInfo *i, gboolean left, gboolean right); + +int lassi_grab_move_pointer_relative(LassiGrabInfo *i, int dx, int dy); +int lassi_grab_press_button(LassiGrabInfo *i, unsigned button, gboolean is_press); +int lassi_grab_press_key(LassiGrabInfo *i, unsigned key, gboolean is_press); + +#endif diff --git a/src/lassi-order.c b/src/lassi-order.c new file mode 100644 index 0000000..ceeb3f1 --- /dev/null +++ b/src/lassi-order.c @@ -0,0 +1,147 @@ +#include + +#include + +#include "lassi-order.h" + +int lassi_list_compare(GList *i, GList *j) { + + for (; i && j; i = i->next, j = j->next) { + int c; + + c = strcmp(i->data, j->data); + + if (c) + return c; + + } + + if (i) + return 1; + + if (j) + return -1; + + return 0; +} + +gboolean lassi_list_nodups(GList *l) { + GList *i, *j; + + for (i = l; i; i = i->next) + for (j = i->next; j; j = j->next) + if (strcmp(i->data, j->data) == 0) + return FALSE; + + return TRUE; +} + +GList *lassi_list_merge(GList *a, GList *b) { + GList *ia, *ib, *p, *c, *d; + + g_assert(lassi_list_nodups(a)); + g_assert(lassi_list_nodups(b)); + + p = b; + + for (ia = a; ia; ia = ia->next) { + + for (ib = p; ib; ib = ib->next) { + + if (strcmp(ia->data, ib->data) == 0) { + + /* Found a common entry, hence copy everything since + * the last one we found from b to a */ + + for (c = p; c != ib; c = c->next) { + + /* Before we copy, make sure this entry is not yet + * in a */ + + for (d = a; d; d = d->next) + if (strcmp(c->data, d->data) == 0) + break; + + if (!d) + /* OK, This one is new, let's copy it */ + + a = g_list_insert_before(a, ia, g_strdup(c->data)); + } + + p = ib->next; + } + } + } + + /* Copy the tail */ + for (c = p; c; c = c->next) { + + for (d = a; d; d = d->next) + if (strcmp(c->data, d->data) == 0) + break; + + if (!d) + a = g_list_append(a, g_strdup(c->data)); + } + + g_assert(lassi_list_nodups(a)); + + return a; +} + +GList* lassi_list_copy(GList *l) { + GList *r = NULL; + + for (; l; l = l->next) + r = g_list_prepend(r, g_strdup(l->data)); + + return g_list_reverse(r); +} + +void lassi_list_free(GList *i) { + while (i) { + g_free(i->data); + i = i->next; + } +} + + +#if 0 + +int main(int argc, char *argv[]) { + GList *a = NULL, *b = NULL, *c = NULL, *d = NULL, *i; + + + a = g_list_append(a, "eins"); + a = g_list_append(a, "zwei"); + a = g_list_append(a, "vier"); + a = g_list_append(a, "fünf"); + a = g_list_append(a, "sechs"); + a = g_list_append(a, "acht"); + + b = g_list_append(b, "eins"); + b = g_list_append(b, "zwei"); + b = g_list_append(b, "drei"); + b = g_list_append(b, "vier"); + b = g_list_append(b, "sechs"); + b = g_list_append(b, "acht"); + + c = g_list_append(c, "eins"); + c = g_list_append(c, "sieben"); + c = g_list_append(c, "acht"); + + d = g_list_append(d, "drei"); + d = g_list_append(d, "neun"); + d = g_list_append(d, "zwei"); + + a = lassi_list_merge(a, b); + a = lassi_list_merge(a, c); + a = lassi_list_merge(a, d); + + for (i = a; i; i = i->next) + g_debug("%s", (char*) i->data); + + return 0; +} + +#endif diff --git a/src/lassi-order.h b/src/lassi-order.h new file mode 100644 index 0000000..e41a0d7 --- /dev/null +++ b/src/lassi-order.h @@ -0,0 +1,13 @@ +#ifndef foolassiorderhfoo +#define foolassiorderhfoo + +#include + +int lassi_list_compare(GList *i, GList *j); +gboolean lassi_list_nodups(GList *l); +GList *lassi_list_merge(GList *a, GList *b); +GList *lassi_list_copy(GList *l); +void lassi_list_free(GList *i); + +#endif + diff --git a/src/lassi-osd.c b/src/lassi-osd.c new file mode 100644 index 0000000..ae9a21c --- /dev/null +++ b/src/lassi-osd.c @@ -0,0 +1,144 @@ +#include +#include + +#include + +#include + +#include "lassi-osd.h" + +int lassi_osd_init(LassiOsdInfo *osd) { + GtkWidget *hbox; + GdkColor color; + guint32 cardinal; + GdkDisplay *display; + + g_assert(osd); + + memset(osd, 0, sizeof(*osd)); + + osd->window = gtk_window_new(GTK_WINDOW_POPUP); + gtk_window_set_title(GTK_WINDOW(osd->window), "Mango Lassi OSD"); + gtk_window_stick(GTK_WINDOW(osd->window)); + gtk_window_set_keep_above(GTK_WINDOW(osd->window), TRUE); + gtk_window_set_decorated(GTK_WINDOW(osd->window), FALSE); + gtk_window_set_deletable(GTK_WINDOW(osd->window), FALSE); + gtk_window_set_type_hint(GTK_WINDOW(osd->window), GDK_WINDOW_TYPE_HINT_NOTIFICATION); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(osd->window), TRUE); + gtk_window_set_skip_pager_hint(GTK_WINDOW(osd->window), TRUE); + gtk_window_set_accept_focus(GTK_WINDOW(osd->window), FALSE); + gtk_window_set_focus_on_map(GTK_WINDOW(osd->window), FALSE); + gtk_window_set_gravity(GTK_WINDOW(osd->window), GDK_GRAVITY_SOUTH_WEST); + + osd->label = gtk_label_new("Test"); + gtk_misc_set_padding(GTK_MISC(osd->label), 16, 0); +/* gtk_label_set_line_wrap(GTK_LABEL(osd->label), TRUE); */ + osd->left_icon = gtk_image_new(); + osd->right_icon = gtk_image_new(); + + hbox = gtk_hbox_new(0, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 8); + + gtk_box_pack_start(GTK_BOX(hbox), osd->left_icon, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), osd->label, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(hbox), osd->right_icon, FALSE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(osd->window), hbox); + + gtk_widget_show(hbox); + gtk_widget_show(osd->label); + + gdk_color_parse("#262624", &color); + if (!gdk_colormap_alloc_color(gtk_widget_get_colormap(osd->window), &color, FALSE, FALSE)) + gdk_color_black(gtk_widget_get_colormap(osd->window), &color); + gtk_widget_modify_bg(osd->window, GTK_STATE_NORMAL, &color); + + gtk_widget_realize(GTK_WIDGET(osd->window)); + + cardinal = 0xbfffffff; + display = gdk_drawable_get_display(osd->window->window); + XChangeProperty(GDK_DISPLAY_XDISPLAY(display), + GDK_WINDOW_XID(osd->window->window), + gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_WINDOW_OPACITY"), + XA_CARDINAL, 32, + PropModeReplace, + (guchar *) &cardinal, 1); + + g_debug("WINDOW=%p", osd->window); + + return 0; +} + +void lassi_osd_done(LassiOsdInfo *osd) { + g_assert(osd); + + gtk_widget_destroy(osd->window); + + memset(osd, 0, sizeof(*osd)); +} + +void lassi_osd_set_text(LassiOsdInfo *osd, const char *text, const char *icon_name_left, const char *icon_name_right) { + char *t; + int w, h, max_width; + + g_assert(osd); + g_assert(osd->window); + + g_debug("WINDOW=%p", osd->window); + + g_debug("Showing text '%s'", text); + + t = g_strdup_printf("%s", text); + gtk_label_set_markup(GTK_LABEL(osd->label), t); + g_free(t); + + if (icon_name_left) { + gtk_image_set_from_icon_name(GTK_IMAGE(osd->left_icon), icon_name_left, GTK_ICON_SIZE_DIALOG); + gtk_widget_show(osd->left_icon); + } else + gtk_widget_hide(osd->left_icon); + + if (icon_name_right) { + gtk_image_set_from_icon_name(GTK_IMAGE(osd->right_icon), icon_name_right, GTK_ICON_SIZE_DIALOG); + gtk_widget_show(osd->right_icon); + } else + gtk_widget_hide(osd->right_icon); + + max_width = (gdk_screen_width()*18)/20; + + g_debug("WINDOW=%p", osd->window); + + gtk_widget_set_size_request(osd->window, -1, -1); + + gtk_window_get_size(GTK_WINDOW(osd->window), &w, &h); + + g_debug("WINDOW=%p", osd->window); + + if (w > max_width) { + gtk_widget_set_size_request(osd->window, max_width, -1); + w = max_width; + } + + if (!icon_name_left == !icon_name_right) { + gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_CENTER); + gtk_window_move(GTK_WINDOW(osd->window), (gdk_screen_width() - w)/2, (gdk_screen_height()*9)/10 - h); + } else if (icon_name_left) { + gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_LEFT); + gtk_window_move(GTK_WINDOW(osd->window), gdk_screen_width()/20, (gdk_screen_height()*9)/10 - h); + } else { + gtk_label_set_justify(GTK_LABEL(osd->label), GTK_JUSTIFY_RIGHT); + gtk_window_move(GTK_WINDOW(osd->window), (gdk_screen_width()*19)/20 - w, (gdk_screen_height()*9)/10 - h); + } + + gtk_widget_show(osd->window); + + g_debug("osd shown"); +} + +void lassi_osd_hide(LassiOsdInfo *osd) { + g_assert(osd); + + gtk_widget_hide(osd->window); + + g_debug("osd hidden"); +} diff --git a/src/lassi-osd.h b/src/lassi-osd.h new file mode 100644 index 0000000..2a792eb --- /dev/null +++ b/src/lassi-osd.h @@ -0,0 +1,18 @@ +#ifndef foolassiosdhfoo +#define foolassiosdhfoo + +#include + +typedef struct LassiOsdInfo LassiOsdInfo; + +struct LassiOsdInfo { + GtkWidget *window, *label, *left_icon, *right_icon; +}; + +int lassi_osd_init(LassiOsdInfo *osd); +void lassi_osd_done(LassiOsdInfo *osd); + +void lassi_osd_set_text(LassiOsdInfo *osd, const char *text, const char *icon_name_left, const char *icon_name_right); +void lassi_osd_hide(LassiOsdInfo *osd); + +#endif diff --git a/src/lassi-prefs.c b/src/lassi-prefs.c new file mode 100644 index 0000000..c3adf68 --- /dev/null +++ b/src/lassi-prefs.c @@ -0,0 +1,212 @@ +#include + +#include + +#include "lassi-prefs.h" +#include "lassi-server.h" + +enum { + COLUMN_ICON, + COLUMN_NAME, + COLUMN_GLIST, + N_COLUMNS +}; + + +static void on_add_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { + GtkWidget *d; + + d = aui_service_dialog_new("Choose Desktop to add", GTK_WINDOW(i->dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL); + aui_service_dialog_set_browse_service_types(AUI_SERVICE_DIALOG(d), LASSI_SERVICE_TYPE, NULL); + + if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT) { + char a[AVAHI_ADDRESS_STR_MAX], *t; + + avahi_address_snprint(a, sizeof(a), aui_service_dialog_get_address(AUI_SERVICE_DIALOG(d))); + t = g_strdup_printf("tcp:port=%u,host=%s", aui_service_dialog_get_port(AUI_SERVICE_DIALOG(d)), a); + lassi_server_connect(i->server, t); + g_free(t); + } + + gtk_widget_destroy(d); +} + +static void on_remove_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { + GtkTreeSelection *selection; + GtkTreeIter iter; + char *id; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); + if (id) { + lassi_server_disconnect(i->server, id, TRUE); + g_free(id); + } +} + +static void on_up_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { + GtkTreeSelection *selection; + GtkTreeIter iter; + char *id; + +/* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); + + if (id) { + GList *o = lassi_list_copy(i->server->order); + lissi_list_move_up(o, id); + lassi_server_set_order(i->server, o); + g_free(id); + }*/ +} + +static void on_down_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { +} + +static void on_close_button_clicked(GtkButton *widget, LassiPrefsInfo *i) { + gtk_widget_hide(GTK_WIDGET(i->dialog)); +} + +static void update_sensitive(LassiPrefsInfo *i) { + GtkTreeIter iter; + GtkTreePath *path; + gboolean is_first; + char *id; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + gtk_widget_set_sensitive(i->up_button, FALSE); + gtk_widget_set_sensitive(i->down_button, FALSE); + gtk_widget_set_sensitive(i->remove_button, FALSE); + return; + } + + gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &id, -1); + gtk_widget_set_sensitive(i->remove_button, strcmp(id, i->server->id) != 0); + g_free(id); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(i->list_store), &iter); + + is_first = gtk_tree_path_prev(path); + gtk_widget_set_sensitive(i->up_button, is_first); + if (is_first) + gtk_tree_path_next(path); + + gtk_tree_path_next(path); + gtk_widget_set_sensitive(i->down_button, gtk_tree_model_get_iter(GTK_TREE_MODEL(i->list_store), &iter, path)); + + gtk_tree_path_free(path); +} + +static void on_selection_changed(GtkTreeSelection *selection, LassiPrefsInfo *i) { + update_sensitive(i); +} + +int lassi_prefs_init(LassiPrefsInfo *i, LassiServer *server) { + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + g_assert(i); + g_assert(server); + + memset(i, 0, sizeof(*i)); + i->server = server; + + i->xml = glade_xml_new("mango-lassi.glade", NULL, NULL); + + i->dialog = glade_xml_get_widget(i->xml, "preferences_dialog"); + i->up_button = glade_xml_get_widget(i->xml, "up_button"); + i->down_button = glade_xml_get_widget(i->xml, "down_button"); + i->add_button = glade_xml_get_widget(i->xml, "add_button"); + i->remove_button = glade_xml_get_widget(i->xml, "remove_button"); + i->tree_view = glade_xml_get_widget(i->xml, "tree_view"); + + glade_xml_signal_connect_data(i->xml, "on_add_button_clicked", (GCallback) on_add_button_clicked, i); + glade_xml_signal_connect_data(i->xml, "on_remove_button_clicked", (GCallback) on_remove_button_clicked, i); + glade_xml_signal_connect_data(i->xml, "on_up_button_clicked", (GCallback) on_up_button_clicked, i); + glade_xml_signal_connect_data(i->xml, "on_down_button_clicked", (GCallback) on_down_button_clicked, i); + + glade_xml_signal_connect_data(i->xml, "on_close_button_clicked", (GCallback) on_close_button_clicked, i); + + g_signal_connect(G_OBJECT(i->dialog), "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); + + i->list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_view_set_model(GTK_TREE_VIEW(i->tree_view), GTK_TREE_MODEL(i->list_store)); + + column = gtk_tree_view_column_new_with_attributes("Icon", gtk_cell_renderer_pixbuf_new(), "icon-name", COLUMN_ICON, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(i->tree_view), column); + + column = gtk_tree_view_column_new_with_attributes("Name", gtk_cell_renderer_text_new(), "text", COLUMN_NAME, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(i->tree_view), column); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); + g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(on_selection_changed), i); + + lassi_prefs_update(i); + + return 0; +} + +void lassi_prefs_update(LassiPrefsInfo *i) { + GList *l; + char *selected_item = NULL; + GtkTreeSelection *selection; + GtkTreeIter iter; + + g_assert(i); + + g_message("prefs update"); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(i->tree_view)); + + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) + gtk_tree_model_get(GTK_TREE_MODEL(i->list_store), &iter, COLUMN_NAME, &selected_item, -1); + + gtk_list_store_clear(GTK_LIST_STORE(i->list_store)); + + for (l = i->server->order; l; l = l->next) { + + if (!lassi_server_is_connected(i->server, l->data)) + continue; + + gtk_list_store_append(GTK_LIST_STORE(i->list_store), &iter); + gtk_list_store_set(GTK_LIST_STORE(i->list_store), &iter, + COLUMN_ICON, strcmp(i->server->id, l->data) ? "network-wired" : "user-desktop", + COLUMN_NAME, l->data, + COLUMN_GLIST, l, -1); + + if (selected_item) + if (strcmp(selected_item, l->data) == 0) + gtk_tree_selection_select_iter(selection, &iter); + } + + g_free(selected_item); + + update_sensitive(i); +} + +void lassi_prefs_show(LassiPrefsInfo *i) { + g_assert(i); + + gtk_window_present(GTK_WINDOW(i->dialog)); +} + +void lassi_prefs_done(LassiPrefsInfo *i) { + g_assert(i); + + g_object_unref(G_OBJECT(i->xml)); + g_object_unref(G_OBJECT(i->list_store)); + + memset(i, 0, sizeof(*i)); +} diff --git a/src/lassi-prefs.h b/src/lassi-prefs.h new file mode 100644 index 0000000..a948709 --- /dev/null +++ b/src/lassi-prefs.h @@ -0,0 +1,29 @@ +#ifndef foolassiprefshfoo +#define foolassiprefshfoo + +#include +#include + +typedef struct LassiPrefsInfo LassiPrefsInfo; +struct LassiServer; + +struct LassiPrefsInfo { + struct LassiServer *server; + + GtkWidget *dialog; + GtkWidget *up_button, *down_button, *add_button, *remove_button; + GtkWidget *tree_view; + + GtkListStore *list_store; + + GladeXML *xml; +}; + +#include "lassi-server.h" + +int lassi_prefs_init(LassiPrefsInfo *i, LassiServer *server); +void lassi_prefs_show(LassiPrefsInfo *i); +void lassi_prefs_update(LassiPrefsInfo *i); +void lassi_prefs_done(LassiPrefsInfo *i); + +#endif diff --git a/src/lassi-server.c b/src/lassi-server.c new file mode 100644 index 0000000..f160279 --- /dev/null +++ b/src/lassi-server.c @@ -0,0 +1,1554 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "lassi-server.h" +#include "lassi-grab.h" +#include "lassi-order.h" +#include "lassi-clipboard.h" +#include "lassi-avahi.h" +#include "lassi-tray.h" + +#define LASSI_INTERFACE "org.gnome.MangoLassi" + +#define PORT_MIN 7421 +#define PORT_MAX (PORT_MIN + 50) + +#define CONNECTIONS_MAX 16 + +static void server_disconnect_all(LassiServer *ls, gboolean clear_order); +static void server_send_update_grab(LassiServer *ls, int y); + +static void server_broadcast(LassiServer *ls, DBusMessage *m, LassiConnection *except) { + GList *i; + + g_assert(ls); + g_assert(m); + + for (i = ls->connections; i; i = i->next) { + dbus_bool_t b; + LassiConnection *lc = i->data; + DBusMessage *n; + + if (lc == except || !lc->id) + continue; + + n = dbus_message_copy(m); + g_assert(n); + b = dbus_connection_send(lc->dbus_connection, n, NULL); + g_assert(b); + dbus_message_unref(n); + } +} + +static void server_layout_changed(LassiServer *ls, int y) { + g_assert(ls); + + g_debug("updating layout"); + + lassi_grab_enable_triggers(&ls->grab_info, !!ls->connections_left, !!ls->connections_right); + + if (ls->active_connection) { + char *t; + gboolean to_left = !!g_list_find(ls->connections_left, ls->active_connection); + + t = g_strdup_printf("Mouse and keyboard are being redirected to %s, which is located to the %s of this screen.\n" + "To redirect input back to this screen, press and release both shift keys simultaneously.", + ls->active_connection->id, to_left ? "left" : "right"); + + if (to_left) + lassi_osd_set_text(&ls->osd_info, t, "go-previous", NULL); + else + lassi_osd_set_text(&ls->osd_info, t, NULL, "go-next"); + + lassi_grab_start(&ls->grab_info, to_left); + + } else { + lassi_grab_stop(&ls->grab_info, y); + lassi_osd_hide(&ls->osd_info); + } +} + +static void server_set_order(LassiServer *ls, GList *order) { + GList *l; + gboolean on_left = TRUE; + g_assert(ls); + + lassi_list_free(ls->order); + ls->order = order; + + g_list_free(ls->connections_left); + g_list_free(ls->connections_right); + + ls->connections_left = ls->connections_right = NULL; + + for (l = ls->order; l; l = l->next) { + LassiConnection *lc; + + if (!(lc = g_hash_table_lookup(ls->connections_by_id, l->data))) { + + if (strcmp(ls->id, l->data)) + continue; + } + + g_assert(lc || on_left); + + if (!lc) + on_left = FALSE; + else if (on_left) + ls->connections_left = g_list_prepend(ls->connections_left, lc); + else + ls->connections_right = g_list_prepend(ls->connections_right, lc); + } + + for (l = ls->connections; l; l = l->next) { + LassiConnection *lc = l->data; + + if (!lc->id) + continue; + + if (g_list_find(ls->connections_left, lc)) + continue; + + if (g_list_find(ls->connections_right, lc)) + continue; + + ls->order = g_list_append(ls->order, lc->id); + ls->connections_right = g_list_prepend(ls->connections_right, lc); + } + + ls->connections_right = g_list_reverse(ls->connections_right); + server_layout_changed(ls, -1); + + lassi_prefs_update(&ls->prefs_info); +} + +static void server_dump(LassiServer *ls) { + GList *l; + int n = 0; + + g_assert(ls); + + g_debug("BEGIN Current connections:"); + + g_debug("Displays left of us:"); + for (l = ls->connections_left; l; l = l->next) { + LassiConnection *lc = l->data; + if (!lc->id) + continue; + g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address); + } + + g_debug("Displays right of us:"); + for (l = ls->connections_right; l; l = l->next) { + LassiConnection *lc = l->data; + if (!lc->id) + continue; + g_debug("%2i) %s %s %s", n++, ls->active_connection == lc ? "ACTIVE" : " ", lc->id, lc->address); + } + + if (!ls->active_connection) + g_debug("We're the active connection"); + + g_debug("END"); +} + +static void connection_destroy(LassiConnection *lc) { + g_assert(lc); + + dbus_connection_flush(lc->dbus_connection); + dbus_connection_close(lc->dbus_connection); + dbus_connection_unref(lc->dbus_connection); + g_free(lc->id); + g_free(lc->address); + g_free(lc); +} + +static void server_pick_active_connection(LassiServer *ls) { + LassiConnection *pick; + GList *l; + char *id; + + pick = NULL; + id = ls->id; + + for (l = ls->connections; l; l = l->next) { + LassiConnection *lc = l->data; + + if (!lc->id) + continue; + + if (strcmp(lc->id, id) > 0) { + id = lc->id; + pick = lc; + } + } + + ls->active_connection = pick; + + server_send_update_grab(ls, -1); + server_layout_changed(ls, -1); +} + +static void server_send_update_grab(LassiServer *ls, int y) { + char *active; + DBusMessage *n; + dbus_bool_t b; + gint32 g; + + g_assert(ls); + + active = ls->active_connection ? ls->active_connection->id : ls->id; + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateGrab"); + g_assert(n); + + g = ++ ls->active_generation; + b = dbus_message_append_args( + n, + DBUS_TYPE_INT32, &g, + DBUS_TYPE_STRING, &active, + DBUS_TYPE_INT32, &y, + DBUS_TYPE_INVALID); + g_assert(b); + + server_broadcast(ls, n, NULL); + dbus_message_unref(n); +} + +static void server_send_update_order(LassiServer *ls, LassiConnection *except) { + DBusMessage *n; + dbus_bool_t b; + gint32 g; + DBusMessageIter iter, sub; + GList *l; + + g_assert(ls); + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "UpdateOrder"); + g_assert(n); + + g = ++ ls->order_generation; + b = dbus_message_append_args( + n, + DBUS_TYPE_INT32, &g, + DBUS_TYPE_INVALID); + g_assert(b); + + dbus_message_iter_init_append(n, &iter); + + b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub); + g_assert(b); + + for (l = ls->order; l; l = l->next) { + b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &l->data); + g_assert(b); + } + + b = dbus_message_iter_close_container(&iter, &sub); + g_assert(b); + + server_broadcast(ls, n, except); + dbus_message_unref(n); +} + +int lassi_server_change_grab(LassiServer *ls, gboolean to_left, int y) { + LassiConnection *lc; + GList *l; + + g_assert(ls); + + l = to_left ? ls->connections_left : ls->connections_right; + lc = l ? l->data : NULL; + + if (!lc) + return -1; + + ls->active_connection = lc; + + server_send_update_grab(ls, y); + server_layout_changed(ls, y); + return 0; +} + +int lassi_server_acquire_grab(LassiServer *ls) { + g_assert(ls); + + ls->active_connection = NULL; + + server_send_update_grab(ls, -1); + server_layout_changed(ls, -1); + return 0; +} + +int lassi_server_motion_event(LassiServer *ls, int dx, int dy) { + DBusMessage *n; + dbus_bool_t b; + + g_assert(ls); + + if (!ls->active_connection) + return -1; + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "MotionEvent"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID); + g_assert(b); + + b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); + g_assert(b); + + dbus_message_unref(n); + + dbus_connection_flush(ls->active_connection->dbus_connection); + + return 0; +} + +int lassi_server_button_event(LassiServer *ls, unsigned button, gboolean is_press) { + DBusMessage *n; + dbus_bool_t b; + + if (!ls->active_connection) + return -1; + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "ButtonEvent"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID); + g_assert(b); + + b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); + g_assert(b); + + dbus_message_unref(n); + + dbus_connection_flush(ls->active_connection->dbus_connection); + + return 0; +} + +int lassi_server_key_event(LassiServer *ls, unsigned key, gboolean is_press) { + DBusMessage *n; + dbus_bool_t b; + + if (!ls->active_connection) + return -1; + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "KeyEvent"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID); + g_assert(b); + + b = dbus_connection_send(ls->active_connection->dbus_connection, n, NULL); + g_assert(b); + + dbus_message_unref(n); + + dbus_connection_flush(ls->active_connection->dbus_connection); + + return 0; +} + +static void show_welcome(LassiConnection *lc, gboolean connect) { + gboolean to_left; + LassiServer *ls; + char *summary, *body; + + g_assert(lc); + + ls = lc->server; + to_left = !!g_list_find(ls->connections_left, lc); + + if (connect) { + summary = g_strdup_printf("%s now shares input with this desktop", lc->id); + body = g_strdup_printf("You're now sharing keyboard and mouse with %s which is located to the %s.", lc->id, to_left ? "left" : "right"); + } else { + summary = g_strdup_printf("%s no longer shares input with this desktop", lc->id); + body = g_strdup_printf("You're no longer sharing keyboard and mouse with %s which was located to the %s.", lc->id, to_left ? "left" : "right"); + } + + lassi_tray_show_notification(&ls->tray_info, summary, body, to_left ? LASSI_TRAY_NOTIFICATION_LEFT : LASSI_TRAY_NOTIFICATION_RIGHT); + + g_free(summary); + g_free(body); +} + +static void connection_unlink(LassiConnection *lc, gboolean remove_from_order) { + LassiServer *ls; + g_assert(lc); + + g_debug("Unlinking %s (%s)", lc->id, lc->address); + + ls = lc->server; + + if (lc->id) { + DBusMessage *n; + dbus_bool_t b; + + /* Tell everyone */ + n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved"); + g_assert(n); + + b = dbus_message_append_args(n, + DBUS_TYPE_STRING, &lc->id, + DBUS_TYPE_STRING, &lc->address, + DBUS_TYPE_BOOLEAN, &remove_from_order, + DBUS_TYPE_INVALID); + g_assert(b); + + server_broadcast(ls, n, NULL); + dbus_message_unref(n); + } + + ls->connections = g_list_remove(ls->connections, lc); + ls->n_connections --; + + if (lc->id) { + show_welcome(lc, FALSE); + + g_hash_table_remove(ls->connections_by_id, lc->id); + ls->connections_left = g_list_remove(ls->connections_left, lc); + ls->connections_right = g_list_remove(ls->connections_right, lc); + + if (ls->active_connection == lc) + server_pick_active_connection(ls); + + if (ls->clipboard_connection == lc) { + ls->clipboard_connection = NULL; + ls->clipboard_empty = TRUE; + lassi_clipboard_clear(&lc->server->clipboard_info, FALSE); + } + + if (ls->primary_connection == lc) { + ls->primary_connection = NULL; + ls->primary_empty = TRUE; + lassi_clipboard_clear(&lc->server->clipboard_info, TRUE); + } + + if (remove_from_order) { + GList *i = g_list_find_custom(ls->order, lc->id, (GCompareFunc) strcmp); + + if (i) + ls->order = g_list_delete_link(ls->order, i); + } + + server_layout_changed(ls, -1); + lassi_prefs_update(&ls->prefs_info); + server_dump(ls); + } + + lassi_tray_update(&ls->tray_info, ls->n_connections); + + connection_destroy(lc); +} + +static void server_position_connection(LassiServer *ls, LassiConnection *lc) { + GList *l; + LassiConnection *last = NULL; + + g_assert(ls); + g_assert(lc); + + g_assert(!g_list_find(ls->connections_left, lc)); + g_assert(!g_list_find(ls->connections_right, lc)); + + for (l = ls->order; l; l = l->next) { + LassiConnection *k; + + if (strcmp(l->data, lc->id) == 0) + break; + + if ((k = g_hash_table_lookup(ls->connections_by_id, l->data))) + last = k; + } + + if (l) { + /* OK, We found a spot to add this */ + + if (last) { + GList *j; + + /*Ok, this one belongs to the right of 'last' */ + + if ((j = g_list_find(ls->connections_left, last))) + /* This one belongs in the left list */ + ls->connections_left = g_list_insert_before(ls->connections_left, j, lc); + else { + /* This one belongs in the rightlist */ + ls->connections_right = g_list_reverse(ls->connections_right); + j = g_list_find(ls->connections_right, last); + g_assert(j); + ls->connections_right = g_list_insert_before(ls->connections_right, j, lc); + ls->connections_right = g_list_reverse(ls->connections_right); + } + } else + /* Hmm, this is before the left end */ + ls->connections_left = g_list_append(ls->connections_left, lc); + } else { + ls->order = g_list_append(ls->order, g_strdup(lc->id)); + /* No spot found, let's add it to the right end */ + ls->connections_right = g_list_append(ls->connections_right, lc); + } +} + +int lassi_server_acquire_clipboard(LassiServer *ls, gboolean primary, char**targets) { + DBusMessageIter iter, sub; + DBusMessage *n; + gint32 g; + gboolean b; + + g_assert(ls); + g_assert(targets); + + if (primary) { + ls->primary_empty = FALSE; + ls->primary_connection = NULL; + } else { + ls->clipboard_empty = FALSE; + ls->clipboard_connection = NULL; + } + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "AcquireClipboard"); + g_assert(n); + + if (primary) + g = ++ ls->primary_generation; + else + g = ++ ls->clipboard_generation; + + b = dbus_message_append_args(n, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID); + g_assert(b); + + dbus_message_iter_init_append(n, &iter); + + b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub); + g_assert(b); + + for (; *targets; targets++) { + g_debug("Exporting target %s", *targets); + b = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, targets); + g_assert(b); + } + + b = dbus_message_iter_close_container(&iter, &sub); + g_assert(b); + + server_broadcast(ls, n, NULL); + g_assert(b); + + dbus_message_unref(n); + return 0; +} + +int lassi_server_return_clipboard(LassiServer *ls, gboolean primary) { + DBusMessage *n; + guint32 g; + gboolean b; + + g_assert(ls); + + if (primary) { + + if (ls->primary_empty || ls->primary_connection != NULL) + return -1; + + ls->primary_empty = TRUE; + ls->primary_connection = NULL; + + } else { + + if (ls->clipboard_empty || ls->clipboard_connection != NULL) + return -1; + + ls->clipboard_empty = TRUE; + ls->clipboard_connection = NULL; + } + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "ReturnClipboard"); + g_assert(n); + + if (primary) + g = ++ ls->primary_generation; + else + g = ++ ls->clipboard_generation; + + b = dbus_message_append_args(n, DBUS_TYPE_UINT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID); + g_assert(b); + + server_broadcast(ls, n, NULL); + + dbus_message_unref(n); + return 0; +} + +int lassi_server_get_clipboard(LassiServer *ls, gboolean primary, const char *t, int *f, gpointer *p, int *l) { + DBusMessage *n, *reply; + DBusConnection *c; + DBusError e; + int ret = -1; + DBusMessageIter iter, sub; + gboolean b; + + g_assert(ls); + + dbus_error_init(&e); + + if (primary) { + + if (ls->primary_empty || !ls->primary_connection) + return -1; + + c = ls->primary_connection->dbus_connection; + + } else { + + if (ls->clipboard_empty || !ls->clipboard_connection) + return -1; + + c = ls->clipboard_connection->dbus_connection; + } + + n = dbus_message_new_method_call(NULL, "/", LASSI_INTERFACE, "GetClipboard"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &t, DBUS_TYPE_INVALID); + g_assert(b); + + if (!(reply = dbus_connection_send_with_reply_and_block(c, n, -1, &e))) { + g_debug("Getting clipboard failed: %s", e.message); + goto finish; + } + + dbus_message_iter_init(reply, &iter); + dbus_message_iter_get_basic(&iter, f); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE) { + g_debug("Invalid clipboard data"); + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_get_fixed_array(&sub, p, l); + + *p = g_memdup(*p, *l); + + ret = 0; + +finish: + + dbus_message_unref(n); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&e); + + return ret; +} + +static int signal_hello(LassiConnection *lc, DBusMessage *m) { + const char *id, *address; + DBusError e; + GList *i; + dbus_bool_t b; + DBusMessage *n; + gint32 active_generation, order_generation, clipboard_generation; + + dbus_error_init(&e); + + if (lc->id) { + g_debug("Received duplicate HelloNode."); + return -1; + } + + if (!(dbus_message_get_args( + m, &e, + DBUS_TYPE_STRING, &id, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INT32, &active_generation, + DBUS_TYPE_INT32, &order_generation, + DBUS_TYPE_INT32, &clipboard_generation, + DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if (strcmp(id, lc->server->id) == 0) { + g_debug("Dropping looped back connection."); + return -1; + } + + if (g_hash_table_lookup(lc->server->connections_by_id, id)) { + g_debug("Dropping duplicate connection."); + return -1; + } + + lc->server->active_generation = MAX(lc->server->active_generation, active_generation); + lc->server->order_generation = MAX(lc->server->order_generation, order_generation); + lc->server->clipboard_generation = MAX(lc->server->clipboard_generation, clipboard_generation); + + g_debug("Got welcome from %s (%s)", id, address); + + lc->id = g_strdup(id); + lc->address = g_strdup(address); + g_hash_table_insert(lc->server->connections_by_id, lc->id, lc); + server_position_connection(lc->server, lc); + + /* Notify all old nodes of the new one */ + n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); + g_assert(b); + + server_broadcast(lc->server, n, lc); + dbus_message_unref(n); + + /* Notify new node about old nodes */ + for (i = lc->server->connections; i; i = i->next) { + LassiConnection *k = i->data; + dbus_bool_t b; + + if (k == lc || !k->id) + continue; + + n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeAdded"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); + g_assert(b); + + b = dbus_connection_send(lc->dbus_connection, n, NULL); + g_assert(b); + + dbus_message_unref(n); + } + + if (lc->we_are_client) { + server_send_update_grab(lc->server, -1); + server_send_update_order(lc->server, NULL); + + lc->delayed_welcome = FALSE; + show_welcome(lc, TRUE); + } else + lc->delayed_welcome = TRUE; + + server_layout_changed(lc->server, -1); + lassi_prefs_update(&lc->server->prefs_info); + + server_dump(lc->server); + + return 0; +} + +static int signal_node_added(LassiConnection *lc, DBusMessage *m) { + const char *id, *address; + DBusError e; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if (strcmp(id, lc->server->id) == 0) + return 0; + + if (g_hash_table_lookup(lc->server->connections_by_id, id)) + return 0; + + if (!(lassi_server_connect(lc->server, address))) { + DBusMessage *n; + dbus_bool_t b; + + /* Failed to connnect to this client, tell everyone */ + n = dbus_message_new_signal("/", LASSI_INTERFACE, "NodeRemoved"); + g_assert(n); + + b = dbus_message_append_args(n, DBUS_TYPE_STRING, &id, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); + g_assert(b); + + server_broadcast(lc->server, n, NULL); + dbus_message_unref(n); + } + + return 0; +} + +static int signal_node_removed(LassiConnection *lc, DBusMessage *m) { + const char *id, *address; + DBusError e; + LassiConnection *k; + gboolean remove_from_order; + LassiServer *ls; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, + DBUS_TYPE_STRING, &id, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_BOOLEAN, &remove_from_order, + DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if (strcmp(id, lc->server->id) == 0) { + g_debug("We've been kicked ourselves."); + + server_disconnect_all(lc->server, TRUE); + return 0; + } + + if (remove_from_order) { + GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); + + if (i) + ls->order = g_list_delete_link(ls->order, i); + } + + ls = lc->server; + + if ((k = g_hash_table_lookup(lc->server->connections_by_id, id))) + connection_unlink(k, remove_from_order); + + server_broadcast(ls, m, lc == k ? NULL : lc); + + return 0; +} + +static int signal_update_grab(LassiConnection *lc, DBusMessage *m) { + const char*id, *current_id; + gint32 generation; + LassiConnection *k = NULL; + DBusError e; + int y; + + dbus_error_init(&e); + + if (!(dbus_message_get_args( + m, &e, + DBUS_TYPE_INT32, &generation, + DBUS_TYPE_STRING, &id, + DBUS_TYPE_INT32, &y, + DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + g_debug("received grab request for %s (%i vs %i)", id, lc->server->active_generation, generation); + + if (strcmp(id, lc->server->id) && !(k = g_hash_table_lookup(lc->server->connections_by_id, id))) { + g_debug("Unknown connection"); + return -1; + } + + if (k == lc->server->active_connection) { + g_debug("Connection already active"); + return 0; + } + + current_id = lc->server->active_connection ? lc->server->active_connection->id : lc->server->id; + + if ((lc->server->active_generation > generation || (lc->server->active_generation == generation && strcmp(current_id, id) > 0))) { + g_debug("Ignoring request for active connection"); + return 0; + } + + lc->server->active_connection = k; + lc->server->active_generation = generation; + + if (!k) + g_debug("We're now the active server."); + else + g_debug("Connection '%s' activated.", k->id); + + server_broadcast(lc->server, m, lc); + server_layout_changed(lc->server, y); + + return 0; +} + +static int signal_update_order(LassiConnection *lc, DBusMessage *m) { + gint32 generation; + DBusError e; + DBusMessageIter iter, sub; + GList *new_order = NULL, *merged_order = NULL; + int r = 0; + int c = 0; + + dbus_error_init(&e); + + if (!(dbus_message_get_args( + m, &e, + DBUS_TYPE_INT32, &generation, + DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + dbus_message_iter_init(m, &iter); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) { + g_debug("Bad connection list fo the left"); + return -1; + } + + if (lc->server->order_generation > generation) { + g_debug("Ignoring request for layout"); + return 0; + } + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *id; + dbus_message_iter_get_basic(&sub, &id); + new_order = g_list_prepend(new_order, g_strdup(id)); + dbus_message_iter_next(&sub); + } + + new_order = g_list_reverse(new_order); + + if (!lassi_list_nodups(new_order)) { + g_debug("Received invalid list."); + r = -1; + goto finish; + } + + c = lassi_list_compare(lc->server->order, new_order); + + if (c == 0) { + g_debug("Requested order identical to ours."); + goto finish; + } + + if (lc->server->order_generation == generation && c > 0) { + g_debug("Ignoring request for layout 2"); + goto finish; + } + + merged_order = lassi_list_merge(lassi_list_copy(new_order), lc->server->order); + + if (lassi_list_compare(lc->server->order, merged_order)) { + server_set_order(lc->server, merged_order); + merged_order = NULL; + } + + server_send_update_order(lc->server, lassi_list_compare(lc->server->order, new_order) ? NULL : lc); + + lc->server->order_generation = generation; + +finish: + + lassi_list_free(new_order); + lassi_list_free(merged_order); + + if (lc->delayed_welcome) { + lc->delayed_welcome = FALSE; + show_welcome(lc, TRUE); + } + + return r; +} + +static int signal_key_event(LassiConnection *lc, DBusMessage *m) { + DBusError e; + guint32 key; + gboolean is_press; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &key, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + +/* g_debug("got dbus key %i %i", key, !!is_press); */ + lassi_grab_press_key(&lc->server->grab_info, key, is_press); + + return 0; +} + +static int signal_motion_event(LassiConnection *lc, DBusMessage *m) { + DBusError e; + int dx, dy; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &dx, DBUS_TYPE_INT32, &dy, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + +/* g_debug("got dbus motion %i %i", dx, dy); */ + lassi_grab_move_pointer_relative(&lc->server->grab_info, dx, dy); + + return 0; +} + +static int signal_button_event(LassiConnection *lc, DBusMessage *m) { + DBusError e; + guint32 button; + gboolean is_press; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_UINT32, &button, DBUS_TYPE_BOOLEAN, &is_press, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + +/* g_debug("got dbus button %i %i", button, !!is_press); */ + lassi_grab_press_button(&lc->server->grab_info, button, is_press); + + return 0; +} + +static int signal_acquire_clipboard(LassiConnection *lc, DBusMessage *m) { + DBusError e; + gint32 g; + gboolean primary; + DBusMessageIter iter, sub; + char **targets; + unsigned alloc_targets, j; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) { + g_debug("Ignoring request for clipboard."); + return 0; + } + + /* FIXME, tie break missing */ + + dbus_message_iter_init(m, &iter); + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) { + g_debug("Bad target list"); + return -1; + } + + dbus_message_iter_recurse(&iter, &sub); + + targets = g_new(char*, alloc_targets = 20); + j = 0; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *t; + dbus_message_iter_get_basic(&sub, &t); + + if (j >= alloc_targets) { + alloc_targets *= 2; + targets = g_realloc(targets, sizeof(char*) * (alloc_targets+1)); + } + + g_assert(j < alloc_targets); + + targets[j++] = (char*) t; + dbus_message_iter_next(&sub); + + g_debug("Received target %s on %s", t, lc->id); + } + + targets[j] = NULL; + + lassi_clipboard_set(&lc->server->clipboard_info, primary, targets); + + g_free(targets); + + if (primary) { + lc->server->primary_connection = lc; + lc->server->primary_empty = FALSE; + lc->server->primary_generation = g; + } else { + lc->server->clipboard_connection = lc; + lc->server->clipboard_empty = FALSE; + lc->server->clipboard_generation = g; + } + + return 0; +} + +static int signal_return_clipboard(LassiConnection *lc, DBusMessage *m) { + DBusError e; + gint32 g; + gboolean primary; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_INT32, &g, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if ((primary ? lc->server->primary_generation : lc->server->clipboard_generation) > g) { + g_debug("Ignoring request for clipboard empty."); + return 0; + } + + /* FIXME, tie break missing */ + + lassi_clipboard_clear(&lc->server->clipboard_info, primary); + + if (primary) { + lc->server->primary_connection = NULL; + lc->server->primary_empty = TRUE; + lc->server->primary_generation = g; + } else { + lc->server->clipboard_connection = NULL; + lc->server->clipboard_empty = TRUE; + lc->server->clipboard_generation = g; + } + + return 0; +} + +static int method_get_clipboard(LassiConnection *lc, DBusMessage *m) { + DBusError e; + char *type; + gboolean primary; + DBusMessage *n = NULL; + gint32 f; + gpointer p = NULL; + int l = 0; + DBusMessageIter iter, sub; + gboolean b; + + dbus_error_init(&e); + + if (!(dbus_message_get_args(m, &e, DBUS_TYPE_BOOLEAN, &primary, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID))) { + g_debug("Received invalid message: %s", e.message); + dbus_error_free(&e); + return -1; + } + + if ((primary && (lc->server->primary_connection || lc->server->primary_empty)) || + (!primary && (lc->server->clipboard_connection || lc->server->clipboard_empty))) { + n = dbus_message_new_error(m, LASSI_INTERFACE ".NotOwner", "We're not the clipboard owner"); + goto finish; + } + + if (lassi_clipboard_get(&lc->server->clipboard_info, primary, type, &f, &p, &l) < 0) { + n = dbus_message_new_error(m, LASSI_INTERFACE ".ClipboardFailure", "Failed to read clipboard data"); + goto finish; + } + + if (l > dbus_connection_get_max_message_size(lc->dbus_connection)*9/10) { + n = dbus_message_new_error(m, LASSI_INTERFACE ".TooLarge", "Clipboard data too large"); + goto finish; + } + + n = dbus_message_new_method_return(m); + g_assert(n); + + dbus_message_iter_init_append(n, &iter); + b = dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &f); + g_assert(b); + + b = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub); + g_assert(b); + + b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, l); + g_assert(b); + + b = dbus_message_iter_close_container(&iter, &sub); + g_assert(b); + +finish: + g_assert(n); + + dbus_connection_send(lc->dbus_connection, n, NULL); + dbus_message_unref(n); + + g_free(p); + + return 0; +} + +DBusHandlerResult message_function(DBusConnection *c, DBusMessage *m, void *userdata) { + DBusError e; + LassiConnection *lc = userdata; + + g_assert(c); + g_assert(m); + g_assert(lc); + + dbus_error_init(&e); + +/* g_debug("[%s] interface=%s, path=%s, member=%s serial=%u", */ +/* lc->id, */ +/* dbus_message_get_interface(m), */ +/* dbus_message_get_path(m), */ +/* dbus_message_get_member(m), */ +/* dbus_message_get_serial(m)); */ + + if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) + goto fail; + + else if (dbus_message_is_signal(m, LASSI_INTERFACE, "Hello")) { + if (signal_hello(lc, m) < 0) + goto fail; + + } else if (lc->id) { + + if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeAdded")) { + + if (signal_node_added(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "NodeRemoved")) { + + if (signal_node_removed(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateGrab")) { + + if (signal_update_grab(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "UpdateOrder")) { + + if (signal_update_order(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "KeyEvent")) { + + if (signal_key_event(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "MotionEvent")) { + + if (signal_motion_event(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ButtonEvent")) { + + if (signal_button_event(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "AcquireClipboard")) { + + if (signal_acquire_clipboard(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_signal(m, LASSI_INTERFACE, "ReturnClipboard")) { + + if (signal_return_clipboard(lc, m) < 0) + goto fail; + + } else if (dbus_message_is_method_call(m, LASSI_INTERFACE, "GetClipboard")) { + + if (method_get_clipboard(lc, m) < 0) + goto fail; + + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + + dbus_error_free(&e); + + connection_unlink(lc, TRUE); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static LassiConnection* connection_add(LassiServer *ls, DBusConnection *c, gboolean we_are_client) { + LassiConnection *lc; + dbus_bool_t b; + DBusMessage *m; + gint32 ag, og, cg; + int fd, one = 1; + + g_assert(ls); + g_assert(c); + + lc = g_new(LassiConnection, 1); + lc->dbus_connection = dbus_connection_ref(c); + lc->server = ls; + lc->id = lc->address = NULL; + lc->we_are_client = we_are_client; + lc->delayed_welcome = FALSE; + ls->connections = g_list_prepend(ls->connections, lc); + ls->n_connections++; + + dbus_connection_setup_with_g_main(c, NULL); + + b = dbus_connection_add_filter(c, message_function, lc, NULL); + g_assert(b); + + m = dbus_message_new_signal("/", LASSI_INTERFACE, "Hello"); + g_assert(m); + + ag = ls->active_generation; + og = ls->order_generation; + cg = ls->clipboard_generation; + + b = dbus_message_append_args( + m, + DBUS_TYPE_STRING, &ls->id, + DBUS_TYPE_STRING, &ls->address, + DBUS_TYPE_INT32, &ag, + DBUS_TYPE_INT32, &og, + DBUS_TYPE_INT32, &cg, + DBUS_TYPE_INVALID); + g_assert(b); + + fd = -1; + dbus_connection_get_socket(c, &fd); + g_assert(fd >= 0); + + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) + g_warning("Failed to enable TCP_NODELAY"); + + b = dbus_connection_send(c, m, NULL); + g_assert(b); + + dbus_message_unref(m); + + lassi_tray_update(&ls->tray_info, ls->n_connections); + return lc; +} + +static void new_connection(DBusServer *s, DBusConnection *c, void *userdata) { + LassiServer *ls = userdata; + + g_assert(s); + g_assert(c); + + if (ls->n_connections >= CONNECTIONS_MAX) + return; + + dbus_connection_set_allow_anonymous(c, TRUE); + connection_add(ls, c, FALSE); +} + +static int server_init(LassiServer *ls) { + DBusError e; + int r = -1; + guint16 port; + + g_assert(ls); + + memset(ls, 0, sizeof(*ls)); + + dbus_error_init(&e); + + for (port = PORT_MIN; port < PORT_MAX; port++) { + char *t; + + t = g_strdup_printf("tcp:port=%u,host=0.0.0.0", port); + ls->dbus_server = dbus_server_listen(t, &e); + g_free(t); + + if (ls->dbus_server) { + ls->port = port; + break; + } + + if (!dbus_error_has_name(&e, DBUS_ERROR_ADDRESS_IN_USE)) { + g_warning("Failed to create D-Bus server: %s %s", e.message, e.name); + goto finish; + } + + dbus_error_free(&e); + } + + if (!ls->dbus_server) { + g_warning("All ports blocked."); + goto finish; + } + + g_debug("Listening on port %u", port); + + dbus_server_setup_with_g_main(ls->dbus_server, NULL); + dbus_server_set_new_connection_function(ls->dbus_server, new_connection, ls, NULL); + + ls->connections_by_id = g_hash_table_new(g_str_hash, g_str_equal); + + ls->id = g_strdup_printf("%s's desktop on %s", g_get_user_name(), g_get_host_name()); + + if (lassi_avahi_init(&ls->avahi_info, ls) < 0) + goto finish; + + /* The initialization of Avahi might have changed ls->id! */ + + ls->address = dbus_server_get_address(ls->dbus_server); + ls->order = g_list_prepend(NULL, g_strdup(ls->id)); + + if (lassi_grab_init(&ls->grab_info, ls) < 0) + goto finish; + + if (lassi_osd_init(&ls->osd_info) < 0) + goto finish; + + if (lassi_clipboard_init(&ls->clipboard_info, ls) < 0) + goto finish; + + + if (lassi_tray_init(&ls->tray_info, ls) < 0) + goto finish; + + if (lassi_prefs_init(&ls->prefs_info, ls) < 0) + goto finish; + + r = 0; + +finish: + dbus_error_free(&e); + return r; +} + +void lassi_server_disconnect(LassiServer *ls, const char *id, gboolean remove_from_order) { + LassiConnection *lc; + + g_assert(ls); + g_assert(id); + + if ((lc = g_hash_table_lookup(ls->connections_by_id, id))) + connection_unlink(lc, remove_from_order); + else if (remove_from_order) { + GList *i = g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); + + if (i) + ls->order = g_list_delete_link(ls->order, i); + } +} + +static void server_disconnect_all(LassiServer *ls, gboolean clear_order) { + + while (ls->connections) + connection_unlink(ls->connections->data, clear_order); + + if (clear_order) { + lassi_list_free(ls->order); + ls->order = NULL; + } +} + +static void server_done(LassiServer *ls) { + + g_assert(ls); + + if (ls->dbus_server) { + dbus_server_disconnect(ls->dbus_server); + dbus_server_unref(ls->dbus_server); + } + + server_disconnect_all(ls, FALSE); + + if (ls->connections_by_id) + g_hash_table_destroy(ls->connections_by_id); + + g_free(ls->id); + g_free(ls->address); + + lassi_list_free(ls->order); + + lassi_grab_done(&ls->grab_info); + lassi_osd_done(&ls->osd_info); + lassi_clipboard_done(&ls->clipboard_info); + lassi_avahi_done(&ls->avahi_info); + lassi_tray_done(&ls->tray_info); + lassi_prefs_done(&ls->prefs_info); + + memset(ls, 0, sizeof(*ls)); +} + +gboolean lassi_server_is_connected(LassiServer *ls, const char *id) { + g_assert(ls); + g_assert(id); + + return strcmp(id, ls->id) == 0 || g_hash_table_lookup(ls->connections_by_id, id); +} + +gboolean lassi_server_is_known(LassiServer *ls, const char *id) { + g_assert(ls); + g_assert(id); + + return !!g_list_find_custom(ls->order, id, (GCompareFunc) strcmp); +} + +LassiConnection* lassi_server_connect(LassiServer *ls, const char *a) { + DBusError e; + DBusConnection *c; + LassiConnection *lc = NULL; + + dbus_error_init(&e); + + if (ls->n_connections >= CONNECTIONS_MAX) + goto finish; + + if (!(c = dbus_connection_open_private(a, &e))) { + g_warning("Failed to connect to client: %s", e.message); + goto finish; + } + + lc = connection_add(ls, c, TRUE); + +finish: + + if (c) + dbus_connection_unref(c); + + dbus_error_free(&e); + return lc; +} + +int main(int argc, char *argv[]) { + LassiServer ls; + + gtk_init(&argc, &argv); + + memset(&ls, 0, sizeof(ls)); + + if (server_init(&ls) < 0) + goto fail; + + gtk_main(); + +fail: + + server_done(&ls); + + return 0; +} diff --git a/src/lassi-server.h b/src/lassi-server.h new file mode 100644 index 0000000..55849d4 --- /dev/null +++ b/src/lassi-server.h @@ -0,0 +1,84 @@ +#ifndef foolassiserverhfoo +#define foolassiserverhfoo + +#include +#include + +typedef struct LassiServer LassiServer; +typedef struct LassiConnection LassiConnection; + +#include "lassi-grab.h" +#include "lassi-osd.h" +#include "lassi-clipboard.h" +#include "lassi-avahi.h" +#include "lassi-tray.h" +#include "lassi-prefs.h" + +struct LassiServer { + DBusServer *dbus_server; + + char *id, *address; + uint16_t port; + + /* All connections */ + GList *connections; + int n_connections; + + /* Configured connections */ + GHashTable *connections_by_id; + GList *connections_left, *connections_right; /* stored from right to left, resp, left to right */ + + /* Active display management */ + int active_generation; + LassiConnection *active_connection; + + /* Layout management */ + int order_generation; + GList *order; + + /* Clipboard CLIPBOARD management */ + int clipboard_generation; + LassiConnection *clipboard_connection; + gboolean clipboard_empty; + + /* Clipboard PRIMARY management */ + int primary_generation; + LassiConnection *primary_connection; + gboolean primary_empty; + + LassiGrabInfo grab_info; + LassiOsdInfo osd_info; + LassiClipboardInfo clipboard_info; + LassiAvahiInfo avahi_info; + LassiTrayInfo tray_info; + LassiPrefsInfo prefs_info; +}; + +struct LassiConnection { + LassiServer *server; + + DBusConnection *dbus_connection; + char *id, *address; + + gboolean we_are_client; + gboolean delayed_welcome; +}; + +int lassi_server_change_grab(LassiServer *s, gboolean to_left, int y); +int lassi_server_acquire_grab(LassiServer *s); + +int lassi_server_motion_event(LassiServer *s, int dx, int dy); +int lassi_server_button_event(LassiServer *ls, unsigned button, gboolean is_press); +int lassi_server_key_event(LassiServer *ls, unsigned key, gboolean is_press); + +int lassi_server_acquire_clipboard(LassiServer *ls, gboolean primary, char**targets); +int lassi_server_return_clipboard(LassiServer *ls, gboolean primary); +int lassi_server_get_clipboard(LassiServer *ls, gboolean primary, const char *t, int *f, gpointer *p, int *l); + +LassiConnection* lassi_server_connect(LassiServer *ls, const char *a); +void lassi_server_disconnect(LassiServer *ls, const char *id, gboolean remove_from_order); + +gboolean lassi_server_is_connected(LassiServer *ls, const char *id); +gboolean lassi_server_is_known(LassiServer *ls, const char *id); + +#endif diff --git a/src/lassi-tray.c b/src/lassi-tray.c new file mode 100644 index 0000000..72fc310 --- /dev/null +++ b/src/lassi-tray.c @@ -0,0 +1,100 @@ +#include + +#include + +#include + +#include "lassi-tray.h" +#include "lassi-server.h" + +#define ICON_IDLE "network-wired" +#define ICON_BUSY "network-workgroup" + +static void on_prefs_activate(GtkMenuItem *menuitem, LassiTrayInfo *i) { + lassi_prefs_show(&i->server->prefs_info); +} + +static void on_tray_activate(GtkStatusIcon *status_icon, LassiTrayInfo *i) { + gtk_menu_popup(GTK_MENU(i->menu), NULL, NULL, gtk_status_icon_position_menu, i->status_icon, 0, gtk_get_current_event_time()); +} + +static void on_tray_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, LassiTrayInfo *i) { + on_tray_activate(status_icon, i); +} + +int lassi_tray_init(LassiTrayInfo *i, LassiServer *server) { + GtkWidget *item; + g_assert(i); + g_assert(server); + + memset(i, 0, sizeof(*i)); + i->server = server; + + notify_init("Mango Lassi"); + + i->status_icon = gtk_status_icon_new_from_icon_name(ICON_IDLE); + + i->menu = gtk_menu_new(); + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(on_prefs_activate), i); + gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_main_quit), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(i->menu), item); + gtk_widget_show_all(i->menu); + + g_signal_connect(G_OBJECT(i->status_icon), "popup_menu", G_CALLBACK(on_tray_popup_menu), i); + g_signal_connect(G_OBJECT(i->status_icon), "activate", G_CALLBACK(on_tray_activate), i); + + lassi_tray_update(i, 0); + + return 0; +} + +void lassi_tray_update(LassiTrayInfo *i, int n_connected) { + char *t; + g_assert(i); + + gtk_status_icon_set_from_icon_name(i->status_icon, n_connected > 0 ? ICON_BUSY : ICON_IDLE); + + if (n_connected == 0) + t = g_strdup("No desktops connected."); + else if (n_connected == 1) + t = g_strdup("1 desktop connected."); + else + t = g_strdup_printf("%i desktops connected.", n_connected); + + gtk_status_icon_set_tooltip(i->status_icon, t); + + g_free(t); +} + +void lassi_tray_show_notification(LassiTrayInfo *i, char *summary, char *body, LassiTrayNotificationIcon icon) { + + static const char * const icon_name[] = { + [LASSI_TRAY_NOTIFICATION_WELCOME] = "user-desktop", + [LASSI_TRAY_NOTIFICATION_LEFT] = "go-previous", + [LASSI_TRAY_NOTIFICATION_RIGHT] = "go-next" + }; + + NotifyNotification *n; + + n = notify_notification_new_with_status_icon(summary, body, icon_name[icon], i->status_icon); + notify_notification_set_timeout(n, 10000); + notify_notification_set_urgency(n, NOTIFY_URGENCY_LOW); + notify_notification_set_category(n, "network"); + notify_notification_show(n, NULL); + +} + +void lassi_tray_done(LassiTrayInfo *i) { + g_assert(i); + + g_object_unref(G_OBJECT(i->status_icon)); + + notify_uninit(); + + memset(i, 0, sizeof(*i)); +} diff --git a/src/lassi-tray.h b/src/lassi-tray.h new file mode 100644 index 0000000..d316092 --- /dev/null +++ b/src/lassi-tray.h @@ -0,0 +1,32 @@ +#ifndef foolassitrayhfoo +#define foolassitrayhfoo + +#include +#include + +typedef struct LassiTrayInfo LassiTrayInfo; +struct LassiServer; + +typedef enum LassiTrayNotificationIcon { + LASSI_TRAY_NOTIFICATION_WELCOME, + LASSI_TRAY_NOTIFICATION_LEFT, + LASSI_TRAY_NOTIFICATION_RIGHT +} LassiTrayNotificationIcon; + +struct LassiTrayInfo { + struct LassiServer *server; + + GtkStatusIcon *status_icon; + GtkWidget *menu; +}; + +#include "lassi-server.h" + +int lassi_tray_init(LassiTrayInfo *i, LassiServer *server); +void lassi_tray_done(LassiTrayInfo *i); +void lassi_tray_update(LassiTrayInfo *i, int n_connected); + +void lassi_tray_show_notification(LassiTrayInfo *i, char *summary, char *body, LassiTrayNotificationIcon icon); + + +#endif diff --git a/src/mango-lassi.glade b/src/mango-lassi.glade new file mode 100644 index 0000000..2be70a0 --- /dev/null +++ b/src/mango-lassi.glade @@ -0,0 +1,300 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 8 + Input Sharing + GTK_WIN_POS_CENTER_ON_PARENT + user-desktop + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 4 + 12 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-add + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Add + True + + + GTK_PACK_END + 1 + + + + + + + False + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-up + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Up + True + + + GTK_PACK_END + 1 + + + + + + + False + 1 + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-down + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Down + True + + + GTK_PACK_END + 1 + + + + + + + False + 2 + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-remove + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Remove + True + + + False + GTK_PACK_END + 1 + + + + + + + False + 3 + + + + + False + 1 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 4 + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + gtk-dialog-info + 6 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <i>Reorder the desktops you're already sharing mouse and keyboard with, or add new desktops to your session. +Order the desktops in the list above from left to right how they are positioned on your desk. +Please make sure to run Mango Lassi Input Sharing on all computers you want so share input with.</i> + True + GTK_JUSTIFY_CENTER + True + True + 50 + + + 1 + + + + + False + GTK_PACK_END + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-close + True + 0 + + + + + + False + GTK_PACK_END + + + + + + diff --git a/src/x b/src/x new file mode 100755 index 0000000..c3b6d95 --- /dev/null +++ b/src/x @@ -0,0 +1,16 @@ +#!/bin/sh + +exec &> /dev/null + +Xephyr -ac :1 & +disown + +DISPLAY=:1 fluxbox & +disown + +Xephyr -ac :2 & +disown + +DISPLAY=:2 fluxbox & +disown + diff --git a/src/xinput.c b/src/xinput.c new file mode 100644 index 0000000..66f0e3e --- /dev/null +++ b/src/xinput.c @@ -0,0 +1,102 @@ +/************************************************************************ + * + * File: xinput.c + * + * Sample program to access input devices other than the X pointer and + * keyboard using the Input Device extension to X. + * This program creates a window and selects input from it. + * To terminate this program, press button 1 on any device being accessed + * through the extension when the X pointer is in the test window. + * + * To compile this program, use + * "cc xinput.c -I/usr/include/X11R5 -L/usr/lib/X11R5 + * -lXi -lXext -lX11 -o xinput + */ + +#include +#include +#include "stdio.h" + +main() +{ + int i, j, count, ndevices, devcnt=0, devkeyp, devbutp; + Display *display; + Window my; + XEvent event; + XDeviceInfoPtr list, slist; + XInputClassInfo *ip; + XDeviceButtonEvent *b; + XEventClass class[128]; + XDevice *dev, *opendevs[9]; + XAnyClassPtr any; + XKeyInfoPtr K; + + if ((display = XOpenDisplay ("")) == NULL) + { + printf ("No connection to server - Terminating.\n"); + exit(1); + } + my = XCreateSimpleWindow (display, RootWindow(display,0), 100, 100, + 100, 100, 1, BlackPixel(display,0), WhitePixel(display,0)); + XMapWindow (display, my); + XSync(display,0); + + slist=list=(XDeviceInfoPtr) XListInputDevices (display, &ndevices); + for (i=0; iinputclassinfo); + for (j=0; jnum_classes; j++) + { + if (any->class == KeyClass) + { + K = (XKeyInfoPtr) any; + printf ("device %s:\n",list->name); + printf ("num_keys=%d min_keycode=%d max_keycode=%d\n\n", + K->num_keys,K->min_keycode,K->max_keycode); + } + else if (any->class == ButtonClass) + printf ("device %s num_buttons=%d\n\n",list->name, + ((XButtonInfoPtr) any)->num_buttons); + /* + * Increment 'any' to point to the next item in the linked + * list. The length is in bytes, so 'any' must be cast to + * a character pointer before being incremented. + */ + any = (XAnyClassPtr) ((char *) any + any->length); + } + if (1) //list->use != IsXKeyboard &&list->use != IsXPointer) + { + dev = XOpenDevice (display, list->id); + for (ip= dev->classes, j=0; jnum_classes; j++, ip++) + if (ip->input_class == KeyClass) + { + /* This is a macro, the braces are necessary */ + DeviceKeyPress (dev, devkeyp, class[count++]); + } + else if (ip->input_class == ButtonClass) + { + DeviceButtonPress (dev, devbutp,class[count++]); + } + opendevs[devcnt++]=dev; + } + } + XSelectExtensionEvent (display, my, class, count); + for (;;) + { + XNextEvent (display, &event); + if (event.type == devkeyp) + printf ("Device key press event device=%d\n", + ((XDeviceKeyEvent *) &event)->deviceid); + else if (event.type == devbutp) + { + b = (XDeviceButtonEvent * ) &event; + printf ("Device button press event device=%d button=%d\n", + b->deviceid, b->button); + if (b->button==1) + break; + } + } + for (i=0; i /dev/null - -Xephyr -ac :1 & -disown - -DISPLAY=:1 fluxbox & -disown - -Xephyr -ac :2 & -disown - -DISPLAY=:2 fluxbox & -disown - diff --git a/xinput.c b/xinput.c deleted file mode 100644 index 66f0e3e..0000000 --- a/xinput.c +++ /dev/null @@ -1,102 +0,0 @@ -/************************************************************************ - * - * File: xinput.c - * - * Sample program to access input devices other than the X pointer and - * keyboard using the Input Device extension to X. - * This program creates a window and selects input from it. - * To terminate this program, press button 1 on any device being accessed - * through the extension when the X pointer is in the test window. - * - * To compile this program, use - * "cc xinput.c -I/usr/include/X11R5 -L/usr/lib/X11R5 - * -lXi -lXext -lX11 -o xinput - */ - -#include -#include -#include "stdio.h" - -main() -{ - int i, j, count, ndevices, devcnt=0, devkeyp, devbutp; - Display *display; - Window my; - XEvent event; - XDeviceInfoPtr list, slist; - XInputClassInfo *ip; - XDeviceButtonEvent *b; - XEventClass class[128]; - XDevice *dev, *opendevs[9]; - XAnyClassPtr any; - XKeyInfoPtr K; - - if ((display = XOpenDisplay ("")) == NULL) - { - printf ("No connection to server - Terminating.\n"); - exit(1); - } - my = XCreateSimpleWindow (display, RootWindow(display,0), 100, 100, - 100, 100, 1, BlackPixel(display,0), WhitePixel(display,0)); - XMapWindow (display, my); - XSync(display,0); - - slist=list=(XDeviceInfoPtr) XListInputDevices (display, &ndevices); - for (i=0; iinputclassinfo); - for (j=0; jnum_classes; j++) - { - if (any->class == KeyClass) - { - K = (XKeyInfoPtr) any; - printf ("device %s:\n",list->name); - printf ("num_keys=%d min_keycode=%d max_keycode=%d\n\n", - K->num_keys,K->min_keycode,K->max_keycode); - } - else if (any->class == ButtonClass) - printf ("device %s num_buttons=%d\n\n",list->name, - ((XButtonInfoPtr) any)->num_buttons); - /* - * Increment 'any' to point to the next item in the linked - * list. The length is in bytes, so 'any' must be cast to - * a character pointer before being incremented. - */ - any = (XAnyClassPtr) ((char *) any + any->length); - } - if (1) //list->use != IsXKeyboard &&list->use != IsXPointer) - { - dev = XOpenDevice (display, list->id); - for (ip= dev->classes, j=0; jnum_classes; j++, ip++) - if (ip->input_class == KeyClass) - { - /* This is a macro, the braces are necessary */ - DeviceKeyPress (dev, devkeyp, class[count++]); - } - else if (ip->input_class == ButtonClass) - { - DeviceButtonPress (dev, devbutp,class[count++]); - } - opendevs[devcnt++]=dev; - } - } - XSelectExtensionEvent (display, my, class, count); - for (;;) - { - XNextEvent (display, &event); - if (event.type == devkeyp) - printf ("Device key press event device=%d\n", - ((XDeviceKeyEvent *) &event)->deviceid); - else if (event.type == devbutp) - { - b = (XDeviceButtonEvent * ) &event; - printf ("Device button press event device=%d button=%d\n", - b->deviceid, b->button); - if (b->button==1) - break; - } - } - for (i=0; i