├── .gitignore ├── eggmarshalers.list ├── .ycm_extra_conf.py ├── Makefile ├── dg-util.h ├── ggt-gadget-winlist.c ├── ggt-gadget-pager.c ├── ggt-tray.h ├── README.rst ├── ggt-launcher.h ├── ggt.h ├── eggtraymanager.h ├── ggt-gadget-appmenu.c ├── ggt-gadget-clock.c ├── ggt-tray.c ├── ggt-launcher.c ├── ggt.c ├── eggtraymanager.c └── COPYING /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | ggt 3 | .ycm_extra_conf.pyc 4 | eggmarshalers.[hc] 5 | .*.sw[op] 6 | -------------------------------------------------------------------------------- /eggmarshalers.list: -------------------------------------------------------------------------------- 1 | VOID:OBJECT,OBJECT 2 | VOID:OBJECT,STRING,LONG,LONG 3 | VOID:OBJECT,LONG 4 | 5 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:fenc=utf-8 4 | # 5 | # Copyright © 2014 Adrian Perez 6 | # 7 | # Distributed under terms of the MIT license. 8 | 9 | pkgs = ( 10 | "glib-2.0", 11 | "gtk+-3.0", 12 | "gtk+-x11-3.0", 13 | "libwnck-3.0", 14 | "keybinder-3.0", 15 | "x11", 16 | "libbamf3", 17 | "gio-2.0", 18 | ) 19 | 20 | from subprocess import check_output 21 | from shlex import split as sh_split 22 | 23 | def pkg_config_flags(*items): 24 | command = ["pkg-config"] 25 | [command.append(i) for i in items] 26 | command.append("--cflags") 27 | return sh_split(check_output(command)) 28 | 29 | def FlagsForFile(path, **kwarg): 30 | return { 'flags': pkg_config_flags(*pkgs), 'do_cache': True } 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile 3 | # Adrian Perez, 2010-09-18 20:56 4 | # 5 | 6 | SRCS := $(wildcard *.c) eggmarshalers.c 7 | OBJS := $(patsubst %.c,%.o,$(SRCS)) 8 | 9 | PKG_MODULE := glib-2.0 gtk+-3.0 gtk+-x11-3.0 libwnck-3.0 keybinder-3.0 x11 \ 10 | libbamf3 gio-2.0 11 | 12 | PKG_CFLAGS := $(shell pkg-config $(PKG_MODULE) --cflags) 13 | PKG_LDLIBS := $(shell pkg-config $(PKG_MODULE) --libs) 14 | 15 | LDFLAGS += $(PKG_LDLIBS) 16 | CFLAGS += $(PKG_CFLAGS) $(OPT_CFLAGS) \ 17 | -DGSEAL_ENABLE \ 18 | -DGTK_DISABLE_DEPRECATED \ 19 | -DGDK_DISABLE_DEPRECATED \ 20 | -Wall -W -Wno-unused -std=gnu99 21 | 22 | all: ggt 23 | 24 | ggt: $(OBJS) 25 | 26 | $(OBJS): eggmarshalers.h 27 | 28 | clean: 29 | $(RM) $(OBJS) ggt eggmarshalers.h eggmarshalers.c 30 | 31 | 32 | prefix ?= $(HOME)/.local 33 | DESTDIR ?= 34 | 35 | install: ggt 36 | install -d $(DESTDIR)$(prefix)/bin 37 | install -m 755 ggt $(DESTDIR)$(prefix)/bin 38 | 39 | eggmarshalers.h: eggmarshalers.list 40 | glib-genmarshal --header --internal --prefix=_egg_marshal $< > $@ 41 | 42 | eggmarshalers.c: eggmarshalers.list 43 | glib-genmarshal --body --internal --prefix=_egg_marshal $< > $@ 44 | 45 | # vim:ft=make 46 | # 47 | 48 | -------------------------------------------------------------------------------- /dg-util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dg-util.h 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #ifndef DG_UTIL_H 9 | #define DG_UTIL_H 10 | 11 | #ifdef __GNUC__ 12 | # define dg_lmem __attribute__ ((cleanup (dg_lmem_cleanup))) 13 | # define dg_lobj __attribute__ ((cleanup (dg_lobj_cleanup))) 14 | # define dg_lerr __attribute__ ((cleanup (dg_lerr_cleanup))) 15 | # include 16 | 17 | static inline void 18 | dg_lmem_cleanup (void *ptr) 19 | { 20 | gpointer **location = ptr; 21 | if (location && *location) { 22 | g_free (*location); 23 | *location = NULL; 24 | } 25 | } 26 | 27 | static inline void 28 | dg_lobj_cleanup (void *ptr) 29 | { 30 | GObject **location = ptr; 31 | if (location && *location) { 32 | g_object_unref (*location); 33 | *location = NULL; 34 | } 35 | } 36 | 37 | static inline void 38 | dg_lerr_cleanup (void *ptr) 39 | { 40 | GError **location = ptr; 41 | if (location && *location) { 42 | g_error_free (*location); 43 | *location = NULL; 44 | } 45 | } 46 | 47 | #else 48 | # define dg_lmem - GCC or Clang is needed for dg_lmem to work 49 | # define dg_lobj - GCC or Clang is needed for dg_lobj to work 50 | # define dg_lerr - GCC or Clang is needed for dg_lerr to work 51 | #endif 52 | 53 | #endif /* !DG_UTIL_H */ 54 | -------------------------------------------------------------------------------- /ggt-gadget-winlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-gadget-winsel.c 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #define WNCK_I_KNOW_THIS_IS_UNSTABLE /* Needed for Wnck :P */ 22 | 23 | #include "ggt.h" 24 | #include 25 | 26 | GtkWidget* 27 | ggt_winlist_new (GGTraybar *app) 28 | { 29 | GtkWidget *winlist = wnck_tasklist_new (); 30 | 31 | g_assert (app); 32 | 33 | wnck_tasklist_set_grouping (WNCK_TASKLIST (winlist), WNCK_TASKLIST_AUTO_GROUP); 34 | wnck_tasklist_set_include_all_workspaces (WNCK_TASKLIST (winlist), FALSE); 35 | wnck_tasklist_set_switch_workspace_on_unminimize (WNCK_TASKLIST (winlist), FALSE); 36 | 37 | return winlist; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /ggt-gadget-pager.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-gadget-pager.c 3 | * Copyright (C) 2010-2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #define WNCK_I_KNOW_THIS_IS_UNSTABLE /* Needed for Wnck :P */ 22 | 23 | #include "ggt.h" 24 | #include 25 | 26 | GtkWidget* 27 | ggt_pager_new (GGTraybar *app) 28 | { 29 | GtkWidget *pager; 30 | g_assert (app); 31 | 32 | pager = wnck_pager_new (); 33 | wnck_pager_set_display_mode (WNCK_PAGER (pager), WNCK_PAGER_DISPLAY_CONTENT); 34 | wnck_pager_set_orientation (WNCK_PAGER (pager), GTK_ORIENTATION_HORIZONTAL); 35 | wnck_pager_set_show_all (WNCK_PAGER (pager), TRUE); 36 | wnck_pager_set_n_rows (WNCK_PAGER (pager), 1); 37 | gtk_widget_set_size_request (pager, 0, 0); 38 | return pager; 39 | } 40 | -------------------------------------------------------------------------------- /ggt-tray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-tray.h 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #ifndef GGT_TRAY_H 22 | #define GGT_TRAY_H 23 | 24 | #include 25 | 26 | G_BEGIN_DECLS 27 | 28 | #define GGT_TYPE_TRAY (ggt_tray_get_type ()) 29 | #define GGT_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GGT_TYPE_TRAY, GgtTray)) 30 | #define GGT_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GGT_TYPE_TRAY, GgtTrayClass) 31 | #define GGT_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GGT_TYPE_TRAY)) 32 | 33 | typedef struct _GgtTrayClass GgtTrayClass; 34 | typedef struct _GgtTray GgtTray; 35 | 36 | GType ggt_tray_get_type (void); 37 | GtkWidget *ggt_tray_new (void); 38 | 39 | G_END_DECLS 40 | 41 | #endif /* !GGT_TRAY_H */ 42 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | GGT - GNOME-inspired Geeky Traybar 2 | ================================== 3 | 4 | This is a tiny replacement for the GNOME Panel. It was designed to be 5 | simple, light and work nicely with the OpenBox window manager. Currently 6 | it has the following features: 7 | 8 | - Top aligned, full-width panel. 9 | 10 | - Command launcher bound globally to "Alt-F2", like the GNOME run dialog. 11 | 12 | - Application menu. Useful for new GNOME applications. 13 | 14 | - Task list. 15 | 16 | - Pager with desktop previews like the one in the GNOME Panel. 17 | 18 | - System tray for application icons. 19 | 20 | - 24-hour clock with calendar pop-up (date can be found in the tooltip). 21 | 22 | - Multi-monitor (XRandR) support. The panel will occupy only the primary 23 | monitor, and when the screen layout or the number of monitors are changed, 24 | it will adapt itself to the new setup. 25 | 26 | - No configuration. 27 | 28 | To use it with OpenBox, just launch "ggt" from the "autostart.sh" script. 29 | You may want to launch other additional goodies from there as well like 30 | nm-applet, gnome-settings-daemon, gnome-screensaver, gnome-power-manager, etc. 31 | 32 | 33 | Dependencies 34 | ------------ 35 | 36 | You will need the following to build GGTraybar: 37 | 38 | - A recent enough GTK+ (3.8 or newer) -- http://gtk.org 39 | - libwnck, tested with 3.4 -- this is part of GNOME. 40 | - keybinder, tested with 0.3.0 -- http://kaizer.se/wiki/keybinder/ 41 | 42 | 43 | Copyright 44 | --------- 45 | 46 | GGTraybar is (C) 2010-2014 Adrian Perez , see COPYING. 47 | 48 | Portions from other authors: 49 | 50 | - ``eggtraymanager.[hc]``: Copyright (C) 2002 Anders Carlsson 51 | 52 | 53 | -------------------------------------------------------------------------------- /ggt-launcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-launcher.h 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #ifndef GGT_LAUNCHER_H 22 | #define GGT_LAUNCHER_H 23 | 24 | #include 25 | 26 | G_BEGIN_DECLS 27 | 28 | #define GGT_TYPE_LAUNCHER (ggt_launcher_get_type ()) 29 | #define GGT_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GGT_TYPE_LAUNCHER, GgtLauncher)) 30 | #define GGT_LAUNCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GGT_TYPE_LAUNCHER, GgtLauncherClass)) 31 | #define GGT_IS_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GGT_TYPE_LAUNCHER)) 32 | 33 | typedef struct _GgtLauncherClass GgtLauncherClass; 34 | typedef struct _GgtLauncher GgtLauncher; 35 | 36 | GType ggt_launcher_get_type (void); 37 | GtkWidget *ggt_launcher_new (const gchar *keystroke, GtkWindow *window); 38 | void ggt_launcher_hide (GgtLauncher *launcher); 39 | void ggt_launcher_show (GgtLauncher *launcher); 40 | 41 | G_END_DECLS 42 | 43 | #endif /* !GGT_LAUNCHER_H */ 44 | -------------------------------------------------------------------------------- /ggt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt.h 3 | * Copyright (C) 2010 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #ifndef __ggtraybar_h__ 22 | #define __ggtraybar_h__ 23 | 24 | /* 25 | * Defines for setting _NET_WM_STRUT(_PARTIAL) 26 | */ 27 | #define NET_WM_STRUT_LEFT 0 28 | #define NET_WM_STRUT_RIGHT 1 29 | #define NET_WM_STRUT_TOP 2 30 | #define NET_WM_STRUT_BOTTOM 3 31 | #define NET_WM_STRUT_LEFT_START_Y 4 32 | #define NET_WM_STRUT_LEFT_END_Y 5 33 | #define NET_WM_STRUT_RIGHT_START_Y 6 34 | #define NET_WM_STRUT_RIGHT_END_Y 7 35 | #define NET_WM_STRUT_TOP_START_X 8 36 | #define NET_WM_STRUT_TOP_END_X 9 37 | #define NET_WM_STRUT_BOTTOM_START_X 10 38 | #define NET_WM_STRUT_BOTTOM_END_X 11 39 | #define NET_WM_STRUT_NELEM 12 40 | #define NET_WM_STRUT_COMPAT_NELEM 4 41 | 42 | #include 43 | #include 44 | 45 | #ifndef UNUSED 46 | #define UNUSED(v) ((void) (v)) 47 | #endif /* UNUSED */ 48 | 49 | struct _GGTraybar { 50 | GtkWidget *window; 51 | GtkWidget *content; 52 | GdkRectangle primary_monitor; 53 | }; 54 | typedef struct _GGTraybar GGTraybar; 55 | 56 | 57 | GtkWidget* ggt_winsel_new (GGTraybar *app); 58 | GtkWidget* ggt_winlist_new (GGTraybar *app); 59 | GtkWidget* ggt_clock_new (GGTraybar *app); 60 | GtkWidget* ggt_pager_new (GGTraybar *app); 61 | GtkWidget* ggt_appmenu_new (GGTraybar *app); 62 | 63 | #endif /* !__ggtraybar_h__ */ 64 | 65 | -------------------------------------------------------------------------------- /eggtraymanager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * eggtraymanager.h 3 | * 4 | * Copyright (C) 2014 Adrian Perez de Castro 5 | * Copyright (C) 2002 Anders Carlsson 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | #ifndef __EGG_TRAY_MANAGER_H__ 24 | #define __EGG_TRAY_MANAGER_H__ 25 | 26 | #include 27 | #include 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define EGG_TYPE_TRAY_MANAGER (egg_tray_manager_get_type ()) 32 | #define EGG_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManager)) 33 | #define EGG_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) 34 | #define EGG_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_MANAGER)) 35 | #define EGG_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_MANAGER)) 36 | #define EGG_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) 37 | 38 | typedef struct _EggTrayManager EggTrayManager; 39 | typedef struct _EggTrayManagerClass EggTrayManagerClass; 40 | typedef struct _EggTrayManagerChild EggTrayManagerChild; 41 | 42 | struct _EggTrayManager 43 | { 44 | GObject parent_instance; 45 | 46 | Atom opcode_atom; 47 | Atom selection_atom; 48 | Atom message_data_atom; 49 | 50 | GtkWidget *invisible; 51 | GdkScreen *screen; 52 | 53 | GList *messages; 54 | GHashTable *socket_table; 55 | }; 56 | 57 | struct _EggTrayManagerClass 58 | { 59 | GObjectClass parent_class; 60 | 61 | void (* tray_icon_added) (EggTrayManager *manager, 62 | EggTrayManagerChild *child); 63 | void (* tray_icon_removed) (EggTrayManager *manager, 64 | EggTrayManagerChild *child); 65 | 66 | void (* message_sent) (EggTrayManager *manager, 67 | EggTrayManagerChild *child, 68 | const gchar *message, 69 | glong id, 70 | glong timeout); 71 | 72 | void (* message_cancelled) (EggTrayManager *manager, 73 | EggTrayManagerChild *child, 74 | glong id); 75 | 76 | void (* lost_selection) (EggTrayManager *manager); 77 | }; 78 | 79 | GType egg_tray_manager_get_type (void); 80 | 81 | gboolean egg_tray_manager_check_running (GdkScreen *screen); 82 | EggTrayManager *egg_tray_manager_new (void); 83 | gboolean egg_tray_manager_manage_screen (EggTrayManager *manager, 84 | GdkScreen *screen); 85 | gchar *egg_tray_manager_get_child_title (EggTrayManager *manager, 86 | EggTrayManagerChild *child); 87 | 88 | G_END_DECLS 89 | 90 | #endif /* __EGG_TRAY_MANAGER_H__ */ 91 | -------------------------------------------------------------------------------- /ggt-gadget-appmenu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * menuserver.c 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #include "ggt.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dg-util.h" 15 | 16 | 17 | typedef struct { 18 | gint32 app_xid; 19 | gchar *app_name; 20 | gchar *unique_bus_name; 21 | gchar *app_menu_object_path; 22 | GMenuModel *app_menu_model; 23 | } Application; 24 | 25 | 26 | static GHashTable *app_by_xid = NULL; 27 | 28 | 29 | static Application* 30 | make_app (BamfApplication *application, 31 | BamfWindow *window) 32 | { 33 | Application app = { 34 | .app_xid = bamf_window_get_xid (window), 35 | .app_name = NULL, 36 | }; 37 | 38 | app.unique_bus_name = bamf_window_get_utf8_prop (window, 39 | "_GTK_UNIQUE_BUS_NAME"); 40 | if (!app.unique_bus_name) 41 | return NULL; 42 | 43 | const gchar *desktop_path = bamf_application_get_desktop_file (application); 44 | if (desktop_path) { 45 | dg_lobj GDesktopAppInfo *app_info = g_desktop_app_info_new_from_filename (desktop_path); 46 | if (app_info) { 47 | app.app_name = g_strdup (g_app_info_get_name (G_APP_INFO (app_info))); 48 | } 49 | } 50 | if (!app.app_name) { 51 | app.app_name = bamf_view_get_name (BAMF_VIEW (application)); 52 | } 53 | g_assert (app.app_name); 54 | 55 | dg_lobj GDBusConnection *bus = g_bus_get_sync (G_BUS_TYPE_SESSION, 56 | NULL, 57 | NULL); 58 | app.app_menu_object_path = bamf_window_get_utf8_prop (window, 59 | "_GTK_APP_MENU_OBJECT_PATH"); 60 | app.app_menu_model = G_MENU_MODEL (g_dbus_menu_model_get (bus, 61 | app.unique_bus_name, 62 | app.app_menu_object_path)); 63 | 64 | Application *newapp = g_slice_new0 (Application); 65 | memcpy (newapp, &app, sizeof (Application)); 66 | return newapp; 67 | } 68 | 69 | 70 | static void 71 | on_active_window_changed (BamfMatcher *matcher, 72 | BamfWindow *window1, 73 | BamfWindow *window2, 74 | GtkWidget *button) 75 | { 76 | if (!window2) 77 | return; 78 | 79 | gint32 xid = bamf_window_get_xid (window2); 80 | Application *app = g_hash_table_lookup (app_by_xid, GINT_TO_POINTER (xid)); 81 | if (!app) { 82 | BamfApplication *application = bamf_matcher_get_application_for_window (matcher, window2); 83 | g_assert (application); 84 | app = make_app (application, window2); 85 | if (!app) 86 | return; 87 | g_hash_table_insert (app_by_xid, GINT_TO_POINTER (xid), app); 88 | } 89 | g_assert (app); 90 | gtk_button_set_label (GTK_BUTTON (button), 91 | app->app_name); 92 | gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), 93 | app->app_menu_model); 94 | } 95 | 96 | 97 | GtkWidget* 98 | ggt_appmenu_new (GGTraybar *app) 99 | { 100 | GtkWidget *button = gtk_menu_button_new (); 101 | gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), FALSE); 102 | 103 | BamfMatcher *matcher = bamf_matcher_get_default (); 104 | app_by_xid = g_hash_table_new (g_direct_hash, g_direct_equal); 105 | 106 | g_signal_connect (matcher, 107 | "active-window-changed", 108 | G_CALLBACK (on_active_window_changed), 109 | button); 110 | 111 | /* Pre-populate for the current active window */ 112 | on_active_window_changed (matcher, 113 | NULL, 114 | bamf_matcher_get_active_window (matcher), 115 | button); 116 | 117 | return button; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /ggt-gadget-clock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-gadget-clock.c 3 | * Copyright (C) 2010-2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include "ggt.h" 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | static gint 28 | clock_tick_update (GtkButton *button) 29 | { 30 | gchar buf[20]; 31 | time_t tnow; 32 | struct tm *now; 33 | 34 | g_assert (button); 35 | 36 | time (&tnow); 37 | now = localtime (&tnow); 38 | strftime (buf, 20, "%H:%M", now); 39 | 40 | gtk_button_set_label (button, buf); 41 | 42 | return TRUE; 43 | } 44 | 45 | 46 | static GtkWidget* 47 | make_calendar_window (void) 48 | { 49 | GtkWidget *window = gtk_window_new (GTK_WINDOW_POPUP); 50 | GtkWidget *calendar = gtk_calendar_new (); 51 | 52 | gtk_window_set_default_size (GTK_WINDOW (window), 180, 180); 53 | 54 | gtk_calendar_set_display_options (GTK_CALENDAR (calendar), 55 | GTK_CALENDAR_SHOW_WEEK_NUMBERS | 56 | GTK_CALENDAR_SHOW_DAY_NAMES | 57 | GTK_CALENDAR_SHOW_HEADING); 58 | 59 | gtk_container_set_border_width (GTK_CONTAINER (window), 5); 60 | gtk_container_add (GTK_CONTAINER (window), calendar); 61 | 62 | return window; 63 | } 64 | 65 | 66 | static void 67 | on_calendar_toggle (GtkToggleButton *button, GtkWidget *calendar) 68 | { 69 | gint x, y; 70 | 71 | g_assert (button); 72 | g_assert (calendar); 73 | 74 | if (gtk_toggle_button_get_active (button)) { 75 | GtkAllocation allocation; 76 | gtk_window_set_position (GTK_WINDOW (calendar), GTK_WIN_POS_MOUSE); 77 | gtk_widget_show_all (calendar); 78 | gtk_window_get_position (GTK_WINDOW (calendar), &x, &y); 79 | gtk_widget_get_allocation (GTK_WIDGET (button), &allocation); 80 | gtk_window_move (GTK_WINDOW (calendar), x, allocation.height); 81 | } 82 | else 83 | gtk_widget_hide (calendar); 84 | } 85 | 86 | 87 | static gboolean 88 | on_tooltip_show (GtkWidget *button, 89 | gint x, 90 | gint y, 91 | gboolean keyboardmode, 92 | GtkTooltip *tooltip, 93 | gpointer data) 94 | { 95 | UNUSED (x); 96 | UNUSED (y); 97 | UNUSED (keyboardmode); 98 | UNUSED (data); 99 | 100 | char buf[60]; 101 | time_t tnow; 102 | struct tm *now; 103 | 104 | g_assert (button); 105 | g_assert (tooltip); 106 | 107 | time (&tnow); 108 | now = localtime (&tnow); 109 | strftime (buf, 60, "%x", now); 110 | 111 | gtk_tooltip_set_text (tooltip, buf); 112 | 113 | return TRUE; 114 | } 115 | 116 | 117 | 118 | GtkWidget* 119 | ggt_clock_new (GGTraybar *app) 120 | { 121 | GtkWidget *button = gtk_toggle_button_new_with_label ("HH:MM"); 122 | GtkWidget *calwin = make_calendar_window (); 123 | 124 | g_assert (app); 125 | 126 | gtk_button_set_relief (GTK_BUTTON (button), 127 | GTK_RELIEF_NONE); 128 | 129 | clock_tick_update (GTK_BUTTON (button)); 130 | 131 | g_signal_connect (G_OBJECT (button), "toggled", 132 | G_CALLBACK (on_calendar_toggle), 133 | calwin); 134 | 135 | g_signal_connect (G_OBJECT (button), "query-tooltip", 136 | G_CALLBACK (on_tooltip_show), 137 | NULL); 138 | 139 | gtk_widget_set_has_tooltip (button, TRUE); 140 | gtk_window_set_attached_to (GTK_WINDOW (calwin), button); 141 | 142 | g_timeout_add (1000 * 15, (GSourceFunc) clock_tick_update, button); 143 | 144 | return button; 145 | } 146 | 147 | -------------------------------------------------------------------------------- /ggt-tray.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-tray.c 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include "ggt.h" 22 | #include "ggt-tray.h" 23 | #include "eggtraymanager.h" 24 | 25 | struct _GgtTray { 26 | GtkBin parent_instance; 27 | EggTrayManager *manager; 28 | GtkWidget *box; 29 | }; 30 | 31 | struct _GgtTrayClass { 32 | GtkBinClass parent_class; 33 | }; 34 | 35 | G_DEFINE_TYPE (GgtTray, ggt_tray, GTK_TYPE_BIN) 36 | 37 | 38 | static void 39 | tray_changed (GgtTray *tray) 40 | { 41 | GtkAllocation allocation; 42 | 43 | gtk_widget_get_allocation (tray->box, &allocation); 44 | gtk_widget_set_size_request (tray->box, 45 | allocation.width, 46 | allocation.height); 47 | 48 | gtk_widget_hide (tray->box); 49 | 50 | while (gtk_events_pending ()) 51 | gtk_main_iteration (); 52 | 53 | gtk_widget_show (tray->box); 54 | } 55 | 56 | 57 | static void 58 | tray_icon_added (EggTrayManager *manager, GtkWidget *icon, GgtTray *tray) 59 | { 60 | UNUSED (manager); 61 | 62 | gtk_box_pack_end (GTK_BOX (tray->box), icon, FALSE, FALSE, 0); 63 | gtk_widget_show (icon); 64 | tray_changed (tray); 65 | } 66 | 67 | 68 | static void 69 | tray_icon_removed (EggTrayManager *manager, GtkWidget *icon, GgtTray *tray) 70 | { 71 | UNUSED (manager); 72 | UNUSED (icon); 73 | 74 | tray_changed (tray); 75 | } 76 | 77 | 78 | static void 79 | ggt_tray_finalize (GObject *object) 80 | { 81 | GgtTray *tray = GGT_TRAY (object); 82 | g_object_unref (tray->manager); 83 | g_object_unref (tray->box); 84 | G_OBJECT_CLASS (ggt_tray_parent_class)->finalize (object); 85 | } 86 | 87 | static void 88 | ggt_tray_init (GgtTray *tray) 89 | { 90 | GdkScreen *screen; 91 | 92 | gtk_container_set_border_width (GTK_CONTAINER (tray), 4); 93 | tray->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1); 94 | gtk_container_add (GTK_CONTAINER (tray), tray->box); 95 | 96 | screen = gtk_widget_get_screen (tray->box); 97 | if (egg_tray_manager_check_running (screen)) { 98 | g_warning ("Another tray manager is already running!"); 99 | return; 100 | } 101 | 102 | tray->manager = egg_tray_manager_new (); 103 | if (!egg_tray_manager_manage_screen (tray->manager, screen)) 104 | g_printerr ("EggTrayManager failed to manage the screen"); 105 | 106 | g_signal_connect (tray->manager, 107 | "tray-icon-added", 108 | G_CALLBACK (tray_icon_added), 109 | tray); 110 | 111 | g_signal_connect (tray->manager, 112 | "tray-icon-removed", 113 | G_CALLBACK (tray_icon_removed), 114 | tray); 115 | } 116 | 117 | 118 | static void 119 | count_children (GtkWidget *widget, gpointer data) 120 | { 121 | UNUSED (widget); 122 | 123 | guint *nitems = (guint*) data; 124 | (*nitems)++; 125 | } 126 | 127 | 128 | static void 129 | ggt_tray_get_preferred_width (GtkWidget *widget, 130 | gint *minimum, 131 | gint *natural) 132 | { 133 | GgtTray *tray = GGT_TRAY (widget); 134 | guint nitems = 0; 135 | 136 | gtk_container_foreach (GTK_CONTAINER (tray->box), 137 | count_children, 138 | &nitems); 139 | 140 | *minimum = *natural = 0; 141 | if (nitems > 0) 142 | *minimum = *natural = (16 * nitems) + (gtk_box_get_spacing (GTK_BOX (tray->box)) * (nitems - 1)); 143 | } 144 | 145 | 146 | static void 147 | ggt_tray_get_preferred_height (GtkWidget *widget, 148 | gint *minimum, 149 | gint *natural) 150 | { 151 | GgtTray *tray = GGT_TRAY (widget); 152 | *minimum = *natural = 16; 153 | } 154 | 155 | 156 | static void 157 | ggt_tray_class_init (GgtTrayClass *klass) 158 | { 159 | GTK_WIDGET_CLASS (klass)->get_preferred_height = ggt_tray_get_preferred_height; 160 | GTK_WIDGET_CLASS (klass)->get_preferred_width = ggt_tray_get_preferred_width; 161 | G_OBJECT_CLASS (klass)->finalize = ggt_tray_finalize; 162 | } 163 | 164 | 165 | GtkWidget* 166 | ggt_tray_new (void) 167 | { 168 | return GTK_WIDGET (g_object_new (GGT_TYPE_TRAY, NULL)); 169 | } 170 | -------------------------------------------------------------------------------- /ggt-launcher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt-launcher.c 3 | * Copyright (C) 2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include "ggt.h" 22 | #include "ggt-launcher.h" 23 | #include 24 | 25 | struct _GgtLauncher { 26 | GObject parent_instance; 27 | GtkWidget *widget; 28 | GtkWidget *window; 29 | GtkWidget *window_child; 30 | GtkWidget *entry; 31 | }; 32 | 33 | struct _GgtLauncherClass { 34 | GtkBinClass parent_class; 35 | }; 36 | 37 | G_DEFINE_TYPE (GgtLauncher, ggt_launcher, G_TYPE_OBJECT) 38 | 39 | 40 | static void 41 | ggt_launcher_finalize (GObject *object) 42 | { 43 | GgtLauncher *launcher = GGT_LAUNCHER (object); 44 | g_object_unref (launcher->widget); 45 | g_object_unref (launcher->window); 46 | g_object_unref (launcher->window_child); 47 | G_OBJECT_CLASS (ggt_launcher_parent_class)->finalize (object); 48 | } 49 | 50 | 51 | static void 52 | ggt_launcher_class_init (GgtLauncherClass *klass) 53 | { 54 | G_OBJECT_CLASS (klass)->finalize = ggt_launcher_finalize; 55 | } 56 | 57 | 58 | static void 59 | on_entry_activate (GtkEntry *entry, GgtLauncher *launcher) 60 | { 61 | g_spawn_command_line_async (gtk_entry_get_text (entry), NULL); 62 | ggt_launcher_hide (launcher); 63 | } 64 | 65 | 66 | static gboolean 67 | on_entry_key_release (GtkWidget *entry, GdkEventKey *event, GgtLauncher *launcher) 68 | { 69 | UNUSED (entry); 70 | 71 | if (event->keyval == GDK_KEY_Escape) { 72 | ggt_launcher_hide (launcher); 73 | return TRUE; 74 | } 75 | 76 | return FALSE; 77 | } 78 | 79 | 80 | static void 81 | ggt_launcher_init (GgtLauncher *launcher) 82 | { 83 | GtkWidget *label = gtk_label_new ("Command:"); 84 | launcher->widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); 85 | launcher->entry = gtk_entry_new (); 86 | 87 | g_object_ref (launcher->widget); 88 | 89 | gtk_container_set_border_width (GTK_CONTAINER (launcher->widget), 2); 90 | gtk_box_pack_start (GTK_BOX (launcher->widget), label, FALSE, FALSE, 0); 91 | gtk_box_pack_start (GTK_BOX (launcher->widget), launcher->entry, TRUE, TRUE, 0); 92 | 93 | g_signal_connect (launcher->entry, 94 | "activate", 95 | G_CALLBACK (on_entry_activate), 96 | launcher); 97 | 98 | g_signal_connect (launcher->entry, 99 | "key-release-event", 100 | G_CALLBACK (on_entry_key_release), 101 | launcher); 102 | 103 | gtk_widget_set_name (launcher->widget, "ggt-launcher"); 104 | gtk_widget_set_name (launcher->entry, "ggt-launcher-entry"); 105 | gtk_widget_set_name (label, "ggt-launcher-label"); 106 | gtk_widget_add_events (launcher->entry, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); 107 | } 108 | 109 | 110 | static void 111 | on_key_activated (const char *keystroke, void *user_data) 112 | { 113 | UNUSED (keystroke); 114 | ggt_launcher_show (GGT_LAUNCHER (user_data)); 115 | } 116 | 117 | 118 | GtkWidget* 119 | ggt_launcher_new (const gchar *keystroke, GtkWindow *window) 120 | { 121 | g_return_val_if_fail (keystroke != NULL, NULL); 122 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); 123 | 124 | GgtLauncher *launcher = GGT_LAUNCHER (g_object_new (GGT_TYPE_LAUNCHER, NULL)); 125 | launcher->window = g_object_ref (window); 126 | 127 | if (!keybinder_bind (keystroke, on_key_activated, launcher)) 128 | g_printerr ("Could not bind '%s' keystroke", keystroke); 129 | 130 | return GTK_WIDGET (launcher); 131 | } 132 | 133 | 134 | void 135 | ggt_launcher_show (GgtLauncher *launcher) 136 | { 137 | g_return_if_fail (GGT_IS_LAUNCHER (launcher)); 138 | g_return_if_fail (launcher->window_child == NULL); 139 | 140 | launcher->window_child = gtk_bin_get_child (GTK_BIN (launcher->window)); 141 | g_object_ref (launcher->window_child); 142 | gtk_widget_hide (launcher->window_child); 143 | gtk_container_remove (GTK_CONTAINER (launcher->window), launcher->window_child); 144 | gtk_container_add (GTK_CONTAINER (launcher->window), launcher->widget); 145 | gtk_widget_reparent (launcher->widget, launcher->window); 146 | gtk_widget_show_all (launcher->window); 147 | gtk_window_set_accept_focus (GTK_WINDOW (launcher->window), TRUE); 148 | gtk_window_set_focus (GTK_WINDOW (launcher->window), launcher->entry); 149 | gtk_widget_grab_focus (launcher->entry); 150 | gtk_window_present (GTK_WINDOW (launcher->window)); 151 | gtk_grab_add (launcher->window); 152 | } 153 | 154 | 155 | void 156 | ggt_launcher_hide (GgtLauncher *launcher) 157 | { 158 | g_return_if_fail (GGT_IS_LAUNCHER (launcher)); 159 | g_return_if_fail (launcher->window_child != NULL); 160 | 161 | gtk_widget_hide (launcher->widget); 162 | gtk_container_remove (GTK_CONTAINER (launcher->window), launcher->widget); 163 | gtk_container_add (GTK_CONTAINER (launcher->window), launcher->window_child); 164 | gtk_widget_reparent (launcher->window_child, launcher->window); 165 | gtk_widget_show_all (launcher->window); 166 | gtk_grab_remove (launcher->window); 167 | g_object_unref (launcher->window_child); 168 | launcher->window_child = NULL; 169 | } 170 | -------------------------------------------------------------------------------- /ggt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ggt.c 3 | * Copyright (C) 2010-2014 Adrian Perez 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #define WNCK_I_KNOW_THIS_IS_UNSTABLE /* Needed for Wnck :P */ 22 | 23 | #include "ggt.h" 24 | #include "ggt-tray.h" 25 | #include "ggt-launcher.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | static GdkAtom a_NET_WM_STRUT_PARTIAL = 0; 33 | static GdkAtom a_NET_WM_STRUT = 0; 34 | static GdkAtom a_CARDINAL = 0; 35 | static GdkAtom a_STRING = 0; 36 | 37 | 38 | static void 39 | intern_atoms (void) 40 | { 41 | a_NET_WM_STRUT_PARTIAL = gdk_atom_intern_static_string ("_NET_WM_STRUT_PARTIAL"); 42 | a_NET_WM_STRUT = gdk_atom_intern_static_string ("_NET_WM_STRUT"); 43 | a_CARDINAL = gdk_atom_intern_static_string ("CARDINAL"); 44 | a_STRING = gdk_atom_intern_static_string ("STRING"); 45 | } 46 | 47 | 48 | static void 49 | configure_window (GGTraybar *app) 50 | { 51 | GtkWindow *window; 52 | 53 | g_assert (app); 54 | 55 | window = GTK_WINDOW (app->window); 56 | 57 | gtk_window_stick (window); 58 | gtk_window_set_title (window, "GGTraybar"); 59 | gtk_window_set_role (window, "traybar"); 60 | gtk_window_set_icon_name (window, "gtk-preferences"); 61 | gtk_window_set_wmclass (window, "GGTraybar", "ggtraybar"); 62 | gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DOCK); 63 | gtk_window_set_resizable (window, FALSE); 64 | gtk_window_set_decorated (window, FALSE); 65 | gtk_window_set_focus_on_map (window, FALSE); 66 | gtk_window_set_keep_above (window, TRUE); 67 | gtk_window_set_skip_pager_hint (window, TRUE); 68 | gtk_container_set_border_width (GTK_CONTAINER (window), 0); 69 | } 70 | 71 | 72 | static inline void 73 | set_window_properties (GGTraybar *app) 74 | { 75 | GdkWindow *window; 76 | gulong data[NET_WM_STRUT_NELEM]; 77 | gint window_height; 78 | 79 | g_assert (app); 80 | 81 | gtk_widget_set_size_request (app->window, 82 | app->primary_monitor.width, 83 | 0); 84 | gtk_window_set_default_size (GTK_WINDOW (app->window), 85 | app->primary_monitor.width, 86 | 0); 87 | 88 | gtk_window_get_size (GTK_WINDOW (app->window), NULL, &window_height); 89 | 90 | window = gtk_widget_get_window (GTK_WIDGET (app->window)); 91 | memset (data, 0x00, sizeof (gulong) * NET_WM_STRUT_NELEM); 92 | 93 | data[NET_WM_STRUT_TOP] = window_height; 94 | data[NET_WM_STRUT_TOP_START_X] = app->primary_monitor.x; 95 | data[NET_WM_STRUT_TOP_END_X] = app->primary_monitor.x 96 | + app->primary_monitor.width - 1; 97 | 98 | gdk_property_change (window, 99 | a_NET_WM_STRUT_PARTIAL, 100 | a_CARDINAL, 32, 101 | GDK_PROP_MODE_REPLACE, 102 | (const guchar*) data, 103 | NET_WM_STRUT_NELEM); 104 | 105 | gdk_property_change (window, 106 | a_NET_WM_STRUT, 107 | a_CARDINAL, 32, 108 | GDK_PROP_MODE_REPLACE, 109 | (const guchar*) data, 110 | NET_WM_STRUT_COMPAT_NELEM); 111 | 112 | gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (app->window))); 113 | 114 | gtk_window_move (GTK_WINDOW (app->window), 115 | app->primary_monitor.x, 116 | 0); 117 | 118 | gtk_window_resize (GTK_WINDOW (app->window), 119 | app->primary_monitor.width, 120 | window_height); 121 | } 122 | 123 | 124 | static void 125 | on_monitors_changed (GdkScreen *screen, gpointer data) 126 | { 127 | GGTraybar *app = (GGTraybar*) data; 128 | 129 | g_assert (screen); 130 | g_assert (app); 131 | 132 | gdk_screen_get_monitor_geometry (screen, 133 | gdk_screen_get_primary_monitor (screen), 134 | &app->primary_monitor); 135 | 136 | set_window_properties (app); 137 | } 138 | 139 | 140 | static void 141 | on_window_map_event (GtkWidget *window, GdkEvent *event, gpointer data) 142 | { 143 | UNUSED (window); 144 | UNUSED (event); 145 | set_window_properties ((GGTraybar*) data); 146 | } 147 | 148 | 149 | int 150 | main (int argc, char **argv) 151 | { 152 | GGTraybar app; 153 | 154 | gtk_init (&argc, &argv); 155 | keybinder_init (); 156 | wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER); 157 | 158 | memset (&app, 0x00, sizeof (GGTraybar)); 159 | 160 | app.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 161 | configure_window (&app); 162 | 163 | /* 164 | * Now create a GtkHBox where all gadgets will be added, and then 165 | * proceed to add all desired widgets to it. Note that: 166 | * 167 | * - The set of available gadgets is fixed. 168 | * - The layout of the gadgets is fixed. 169 | * - The order of the gadgets is fixed. 170 | * 171 | * Why? Because I want them that way... and less is more :-) 172 | */ 173 | app.content = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 174 | 175 | #define GADGET(_box, _pos, _gad) \ 176 | do { \ 177 | GtkWidget *gadget = (_gad); \ 178 | if (gadget) \ 179 | gtk_box_pack_ ## _pos (GTK_BOX (_box), gadget, FALSE, FALSE, 0); \ 180 | } while (0) 181 | 182 | GADGET (app.content, start, ggt_appmenu_new (&app)); 183 | GADGET (app.content, end, ggt_clock_new (&app)); 184 | GADGET (app.content, end, ggt_tray_new () ); 185 | GADGET (app.content, end, ggt_winlist_new (&app)); 186 | /* GADGET (app.content, end, ggt_pager_new (&app)); */ 187 | ggt_launcher_new ("F2", GTK_WINDOW (app.window)); 188 | 189 | /* 190 | * Finished adding widgets to the panel. 191 | * Now add the container to the window. 192 | */ 193 | gtk_container_add (GTK_CONTAINER (app.window), 194 | GTK_WIDGET (app.content)); 195 | 196 | /* 197 | * This makes the panel use the same GtkRc styles than those used by the 198 | * GNOME panel, so themes which modify the panel should blend finely. 199 | */ 200 | gtk_widget_set_name (app.window, "PanelWidget"); 201 | 202 | /* 203 | * We need X resources to get created so Gdk knows what to do to intern 204 | * atoms and setting properties: window needs to be realized first! 205 | */ 206 | gtk_widget_realize (app.window); 207 | intern_atoms (); 208 | on_monitors_changed (gtk_widget_get_screen (app.window), &app); 209 | 210 | g_signal_connect (G_OBJECT (gtk_widget_get_screen (app.window)), 211 | "monitors_changed", 212 | G_CALLBACK (on_monitors_changed), 213 | &app); 214 | 215 | g_signal_connect (G_OBJECT (app.window), 216 | "map-event", 217 | G_CALLBACK (on_window_map_event), 218 | &app); 219 | 220 | gtk_widget_show_all (app.window); 221 | gtk_main (); 222 | 223 | return EXIT_SUCCESS; 224 | } 225 | 226 | 227 | -------------------------------------------------------------------------------- /eggtraymanager.c: -------------------------------------------------------------------------------- 1 | /* 2 | * eggtraymanager.c 3 | * 4 | * Copyright (C) 2014 Adrian Perez de Castro 5 | * Copyright (C) 2002 Anders Carlsson 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "eggtraymanager.h" 27 | #include "eggmarshalers.h" 28 | 29 | G_DEFINE_TYPE (EggTrayManager, egg_tray_manager, G_TYPE_OBJECT) 30 | 31 | /* Signals */ 32 | enum 33 | { 34 | TRAY_ICON_ADDED, 35 | TRAY_ICON_REMOVED, 36 | MESSAGE_SENT, 37 | MESSAGE_CANCELLED, 38 | LOST_SELECTION, 39 | LAST_SIGNAL 40 | }; 41 | 42 | typedef struct 43 | { 44 | long id, len; 45 | long remaining_len; 46 | 47 | long timeout; 48 | Window window; 49 | char *str; 50 | } PendingMessage; 51 | 52 | static guint manager_signals[LAST_SIGNAL] = { 0 }; 53 | 54 | #define SYSTEM_TRAY_REQUEST_DOCK 0 55 | #define SYSTEM_TRAY_BEGIN_MESSAGE 1 56 | #define SYSTEM_TRAY_CANCEL_MESSAGE 2 57 | 58 | static gboolean egg_tray_manager_check_running_xscreen (Screen *xscreen); 59 | 60 | static void egg_tray_manager_init (EggTrayManager *manager); 61 | static void egg_tray_manager_class_init (EggTrayManagerClass *klass); 62 | 63 | static void egg_tray_manager_finalize (GObject *object); 64 | 65 | static void egg_tray_manager_unmanage (EggTrayManager *manager); 66 | 67 | 68 | static void 69 | egg_tray_manager_init (EggTrayManager *manager) 70 | { 71 | manager->socket_table = g_hash_table_new (NULL, NULL); 72 | } 73 | 74 | static void 75 | egg_tray_manager_class_init (EggTrayManagerClass *klass) 76 | { 77 | G_OBJECT_CLASS (klass)->finalize = egg_tray_manager_finalize; 78 | 79 | manager_signals[TRAY_ICON_ADDED] = 80 | g_signal_new ("tray-icon-added", 81 | G_OBJECT_CLASS_TYPE (klass), 82 | G_SIGNAL_RUN_LAST, 83 | G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_added), 84 | NULL, NULL, 85 | g_cclosure_marshal_VOID__OBJECT, 86 | G_TYPE_NONE, 1, 87 | GTK_TYPE_SOCKET); 88 | 89 | manager_signals[TRAY_ICON_REMOVED] = 90 | g_signal_new ("tray-icon-removed", 91 | G_OBJECT_CLASS_TYPE (klass), 92 | G_SIGNAL_RUN_LAST, 93 | G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_removed), 94 | NULL, NULL, 95 | g_cclosure_marshal_VOID__OBJECT, 96 | G_TYPE_NONE, 1, 97 | GTK_TYPE_SOCKET); 98 | 99 | manager_signals[MESSAGE_SENT] = 100 | g_signal_new ("message-sent", 101 | G_OBJECT_CLASS_TYPE (klass), 102 | G_SIGNAL_RUN_LAST, 103 | G_STRUCT_OFFSET (EggTrayManagerClass, message_sent), 104 | NULL, NULL, 105 | _egg_marshal_VOID__OBJECT_STRING_LONG_LONG, 106 | G_TYPE_NONE, 4, 107 | GTK_TYPE_SOCKET, 108 | G_TYPE_STRING, 109 | G_TYPE_LONG, 110 | G_TYPE_LONG); 111 | 112 | manager_signals[MESSAGE_CANCELLED] = 113 | g_signal_new ("message-cancelled", 114 | G_OBJECT_CLASS_TYPE (klass), 115 | G_SIGNAL_RUN_LAST, 116 | G_STRUCT_OFFSET (EggTrayManagerClass, message_cancelled), 117 | NULL, NULL, 118 | _egg_marshal_VOID__OBJECT_LONG, 119 | G_TYPE_NONE, 2, 120 | GTK_TYPE_SOCKET, 121 | G_TYPE_LONG); 122 | 123 | manager_signals[LOST_SELECTION] = 124 | g_signal_new ("lost-selection", 125 | G_OBJECT_CLASS_TYPE (klass), 126 | G_SIGNAL_RUN_LAST, 127 | G_STRUCT_OFFSET (EggTrayManagerClass, lost_selection), 128 | NULL, NULL, 129 | g_cclosure_marshal_VOID__VOID, 130 | G_TYPE_NONE, 0); 131 | } 132 | 133 | static void 134 | egg_tray_manager_finalize (GObject *object) 135 | { 136 | egg_tray_manager_unmanage (EGG_TRAY_MANAGER (object)); 137 | G_OBJECT_CLASS (egg_tray_manager_parent_class)->finalize (object); 138 | } 139 | 140 | EggTrayManager * 141 | egg_tray_manager_new (void) 142 | { 143 | return EGG_TRAY_MANAGER (g_object_new (EGG_TYPE_TRAY_MANAGER, NULL)); 144 | } 145 | 146 | static gboolean 147 | egg_tray_manager_plug_removed (GtkSocket *socket, 148 | EggTrayManager *manager) 149 | { 150 | Window *window = g_object_get_data (G_OBJECT (socket), "egg-tray-child-window"); 151 | 152 | g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window)); 153 | g_object_set_data (G_OBJECT (socket), "egg-tray-child-window", NULL); 154 | g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket); 155 | 156 | /* This destroys the socket. */ 157 | return FALSE; 158 | } 159 | 160 | #if 0 161 | static gboolean 162 | egg_tray_manager_socket_draw (GtkWidget *widget, 163 | cairo_t *cr) 164 | { 165 | GtkStyleContext *context = gtk_widget_get_style_context (widget); 166 | gint h = gtk_widget_get_allocated_height (widget); 167 | gint w = gtk_widget_get_allocated_width (widget); 168 | GdkRGBA bg_color; 169 | 170 | gtk_style_context_get_background_color (context, 171 | GTK_STATE_FLAG_NORMAL, 172 | &bg_color); 173 | 174 | cairo_set_source_rgba (cr, 175 | bg_color.red, 176 | bg_color.green, 177 | bg_color.blue, 178 | bg_color.alpha); 179 | 180 | cairo_rectangle (cr, 0, 0, w, h); 181 | cairo_fill (cr); 182 | 183 | return FALSE; 184 | } 185 | #endif 186 | 187 | static void 188 | egg_tray_manager_handle_dock_request(EggTrayManager *manager, 189 | XClientMessageEvent *xevent) 190 | { 191 | GtkWidget *socket; 192 | Window *window; 193 | 194 | socket = gtk_socket_new (); 195 | /* gtk_widget_set_app_paintable (socket, TRUE); 196 | gtk_widget_set_double_buffered (socket, FALSE); 197 | g_signal_connect (socket, "draw", 198 | G_CALLBACK (egg_tray_manager_socket_draw), 199 | NULL); */ 200 | 201 | gtk_widget_show (socket); 202 | 203 | /* We need to set the child window here 204 | * so that the client can call _get functions 205 | * in the signal handler 206 | */ 207 | window = g_new (Window, 1); 208 | *window = xevent->data.l[2]; 209 | 210 | g_object_set_data_full (G_OBJECT (socket), 211 | "egg-tray-child-window", 212 | window, g_free); 213 | 214 | g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, socket); 215 | 216 | /* Add the socket only if it's been attached */ 217 | if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(socket)))) 218 | { 219 | GtkRequisition req; 220 | XWindowAttributes wa; 221 | 222 | gtk_socket_add_id (GTK_SOCKET (socket), xevent->data.l[2]); 223 | g_signal_connect (socket, 224 | "plug-removed", 225 | G_CALLBACK (egg_tray_manager_plug_removed), 226 | manager); 227 | 228 | gdk_error_trap_push (); 229 | XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 230 | *window, 231 | &wa); 232 | if (gdk_error_trap_pop ()) 233 | { 234 | g_signal_emit (manager, 235 | manager_signals[TRAY_ICON_REMOVED], 236 | 0, 237 | socket); 238 | gtk_widget_destroy (socket); 239 | } 240 | else 241 | { 242 | g_hash_table_insert (manager->socket_table, 243 | GINT_TO_POINTER (xevent->data.l[2]), 244 | socket); 245 | req.width = req.height = 1; 246 | gtk_widget_get_preferred_size (socket, &req, NULL); 247 | } 248 | } 249 | } 250 | 251 | static void 252 | pending_message_free (PendingMessage *message) 253 | { 254 | g_free (message->str); 255 | g_free (message); 256 | } 257 | 258 | static void 259 | egg_tray_manager_handle_message_data (EggTrayManager *manager, 260 | XClientMessageEvent *xevent) 261 | { 262 | GList *p; 263 | 264 | /* Try to see if we can find the 265 | * pending message in the list 266 | */ 267 | for (p = manager->messages; p; p = p->next) 268 | { 269 | PendingMessage *msg = p->data; 270 | 271 | if (xevent->window == msg->window) 272 | { 273 | /* Append the message */ 274 | gint len = MIN (msg->remaining_len, 20); 275 | 276 | memcpy (msg->str + msg->len - msg->remaining_len, 277 | &xevent->data, 278 | len); 279 | msg->remaining_len -= len; 280 | 281 | if (msg->remaining_len == 0) 282 | { 283 | GtkSocket *socket = g_hash_table_lookup (manager->socket_table, 284 | GINT_TO_POINTER (msg->window)); 285 | if (socket) 286 | g_signal_emit (manager, 287 | manager_signals[MESSAGE_SENT], 288 | 0, 289 | socket, 290 | msg->str, 291 | msg->id, 292 | msg->timeout); 293 | 294 | manager->messages = g_list_remove_link (manager->messages, p); 295 | pending_message_free (msg); 296 | } 297 | return; 298 | } 299 | } 300 | } 301 | 302 | static void 303 | egg_tray_manager_handle_begin_message (EggTrayManager *manager, 304 | XClientMessageEvent *xevent) 305 | { 306 | GList *p; 307 | 308 | /* 309 | * Check if the same message is already in the queue and remove it if so 310 | */ 311 | for (p = manager->messages; p; p = p->next) 312 | { 313 | PendingMessage *msg = p->data; 314 | 315 | if (xevent->window == msg->window && xevent->data.l[4] == msg->id) 316 | { 317 | /* Hmm, we found it, now remove it */ 318 | pending_message_free (msg); 319 | manager->messages = g_list_remove_link (manager->messages, p); 320 | break; 321 | } 322 | } 323 | 324 | /* Now add the new message to the queue */ 325 | PendingMessage *msg = g_new0 (PendingMessage, 1); 326 | msg->window = xevent->window; 327 | msg->timeout = xevent->data.l[2]; 328 | msg->len = xevent->data.l[3]; 329 | msg->id = xevent->data.l[4]; 330 | msg->remaining_len = msg->len; 331 | msg->str = g_malloc (msg->len + 1); 332 | msg->str[msg->len] = '\0'; 333 | manager->messages = g_list_prepend (manager->messages, msg); 334 | } 335 | 336 | static void 337 | egg_tray_manager_handle_cancel_message (EggTrayManager *manager, 338 | XClientMessageEvent *xevent) 339 | { 340 | GtkSocket *socket = g_hash_table_lookup (manager->socket_table, 341 | GINT_TO_POINTER (xevent->window)); 342 | if (socket) 343 | g_signal_emit (manager, 344 | manager_signals[MESSAGE_CANCELLED], 345 | 0, 346 | socket, 347 | xevent->data.l[2]); 348 | } 349 | 350 | static GdkFilterReturn 351 | egg_tray_manager_handle_event (EggTrayManager *manager, 352 | XClientMessageEvent *xevent) 353 | { 354 | switch (xevent->data.l[1]) 355 | { 356 | case SYSTEM_TRAY_REQUEST_DOCK: 357 | egg_tray_manager_handle_dock_request (manager, xevent); 358 | return GDK_FILTER_REMOVE; 359 | 360 | case SYSTEM_TRAY_BEGIN_MESSAGE: 361 | egg_tray_manager_handle_begin_message (manager, xevent); 362 | return GDK_FILTER_REMOVE; 363 | 364 | case SYSTEM_TRAY_CANCEL_MESSAGE: 365 | egg_tray_manager_handle_cancel_message (manager, xevent); 366 | return GDK_FILTER_REMOVE; 367 | default: 368 | break; 369 | } 370 | 371 | return GDK_FILTER_CONTINUE; 372 | } 373 | 374 | static GdkFilterReturn 375 | egg_tray_manager_window_filter (GdkXEvent *xev, 376 | GdkEvent *event, 377 | gpointer data) 378 | { 379 | XEvent *xevent = (GdkXEvent *)xev; 380 | EggTrayManager *manager = EGG_TRAY_MANAGER (data); 381 | 382 | (void) event; /* Unused, avoid compiler warning */ 383 | 384 | if (xevent->type == ClientMessage) 385 | { 386 | if (xevent->xclient.message_type == manager->opcode_atom) 387 | { 388 | return egg_tray_manager_handle_event (manager, (XClientMessageEvent *)xevent); 389 | } 390 | else if (xevent->xclient.message_type == manager->message_data_atom) 391 | { 392 | egg_tray_manager_handle_message_data (manager, (XClientMessageEvent *)xevent); 393 | return GDK_FILTER_REMOVE; 394 | } 395 | } 396 | else if (xevent->type == SelectionClear) 397 | { 398 | g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); 399 | egg_tray_manager_unmanage (manager); 400 | } 401 | 402 | return GDK_FILTER_CONTINUE; 403 | } 404 | 405 | static void 406 | egg_tray_manager_unmanage (EggTrayManager *manager) 407 | { 408 | if (manager->invisible == NULL) 409 | return; 410 | 411 | GtkWidget *invisible = manager->invisible; 412 | 413 | g_assert (GTK_IS_INVISIBLE (invisible)); 414 | g_assert (gtk_widget_get_realized (invisible)); 415 | 416 | Display *display = GDK_WINDOW_XDISPLAY (gtk_widget_get_window (invisible)); 417 | 418 | if (XGetSelectionOwner (display, manager->selection_atom) == 419 | gdk_x11_window_get_xid (gtk_widget_get_window (invisible))) 420 | { 421 | guint32 timestamp = gdk_x11_get_server_time (gtk_widget_get_window (invisible)); 422 | XSetSelectionOwner (display, manager->selection_atom, None, timestamp); 423 | } 424 | 425 | gdk_window_remove_filter (gtk_widget_get_window (invisible), 426 | egg_tray_manager_window_filter, 427 | manager); 428 | 429 | manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ 430 | gtk_widget_destroy (invisible); 431 | g_object_unref (G_OBJECT (invisible)); 432 | } 433 | 434 | static gboolean 435 | egg_tray_manager_manage_xscreen (EggTrayManager *manager, Screen *xscreen) 436 | { 437 | g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), FALSE); 438 | g_return_val_if_fail (manager->screen == NULL, FALSE); 439 | 440 | /* 441 | * If there's already a manager running on the screen 442 | * we can't create another one. 443 | */ 444 | #if 0 445 | if (egg_tray_manager_check_running_xscreen (xscreen)) 446 | return FALSE; 447 | #endif 448 | GdkScreen *screen = gdk_display_get_screen (gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)), 449 | XScreenNumberOfScreen (xscreen)); 450 | 451 | GtkWidget *invisible = gtk_invisible_new_for_screen (screen); 452 | gtk_widget_realize (invisible); 453 | gtk_widget_add_events (invisible, 454 | GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); 455 | 456 | gchar* selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 457 | XScreenNumberOfScreen (xscreen)); 458 | manager->selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); 459 | g_free (selection_atom_name); 460 | 461 | GdkWindow *invwindow = gtk_widget_get_window (invisible); 462 | guint32 timestamp = gdk_x11_get_server_time (invwindow); 463 | XSetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom, 464 | gdk_x11_window_get_xid (invwindow), timestamp); 465 | 466 | /* Check if we were could set the selection owner successfully */ 467 | if (XGetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom) == 468 | gdk_x11_window_get_xid (invwindow)) 469 | { 470 | XClientMessageEvent xev; 471 | 472 | xev.type = ClientMessage; 473 | xev.window = RootWindowOfScreen (xscreen); 474 | xev.message_type = XInternAtom (DisplayOfScreen (xscreen), "MANAGER", False); 475 | 476 | xev.format = 32; 477 | xev.data.l[0] = timestamp; 478 | xev.data.l[1] = manager->selection_atom; 479 | xev.data.l[2] = gdk_x11_window_get_xid (invwindow); 480 | xev.data.l[3] = 0; /* manager specific data */ 481 | xev.data.l[4] = 0; /* manager specific data */ 482 | 483 | XSendEvent (DisplayOfScreen (xscreen), 484 | RootWindowOfScreen (xscreen), 485 | False, 486 | StructureNotifyMask, 487 | (XEvent *)&xev); 488 | 489 | manager->invisible = invisible; 490 | g_object_ref (G_OBJECT (manager->invisible)); 491 | 492 | manager->opcode_atom = XInternAtom (DisplayOfScreen (xscreen), 493 | "_NET_SYSTEM_TRAY_OPCODE", 494 | False); 495 | 496 | manager->message_data_atom = XInternAtom (DisplayOfScreen (xscreen), 497 | "_NET_SYSTEM_TRAY_MESSAGE_DATA", 498 | False); 499 | 500 | /* Add a window filter */ 501 | gdk_window_add_filter (invwindow, egg_tray_manager_window_filter, manager); 502 | return TRUE; 503 | } 504 | else 505 | { 506 | gtk_widget_destroy (invisible); 507 | return FALSE; 508 | } 509 | } 510 | 511 | gboolean 512 | egg_tray_manager_manage_screen (EggTrayManager *manager, 513 | GdkScreen *screen) 514 | { 515 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 516 | g_return_val_if_fail (manager->screen == NULL, FALSE); 517 | 518 | return egg_tray_manager_manage_xscreen (manager, 519 | GDK_SCREEN_XSCREEN (screen)); 520 | } 521 | 522 | static gboolean 523 | egg_tray_manager_check_running_xscreen (Screen *xscreen) 524 | { 525 | gchar *selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 526 | XScreenNumberOfScreen (xscreen)); 527 | Atom selection_atom = XInternAtom (DisplayOfScreen (xscreen), 528 | selection_atom_name, 529 | False); 530 | g_free (selection_atom_name); 531 | 532 | if (XGetSelectionOwner (DisplayOfScreen (xscreen), selection_atom)) 533 | return TRUE; 534 | else 535 | return FALSE; 536 | } 537 | 538 | gboolean 539 | egg_tray_manager_check_running (GdkScreen *screen) 540 | { 541 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 542 | return egg_tray_manager_check_running_xscreen (GDK_SCREEN_XSCREEN (screen)); 543 | } 544 | 545 | gchar* 546 | egg_tray_manager_get_child_title (EggTrayManager *manager, 547 | EggTrayManagerChild *child) 548 | { 549 | g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), NULL); 550 | g_return_val_if_fail (GTK_IS_SOCKET (child), NULL); 551 | 552 | Window *child_window = g_object_get_data (G_OBJECT (child), 553 | "egg-tray-child-window"); 554 | 555 | Atom utf8_string = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 556 | "UTF8_STRING", 557 | False); 558 | Atom atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 559 | "_NET_WM_NAME", 560 | False); 561 | 562 | gdk_error_trap_push(); 563 | 564 | Atom type; 565 | int format; 566 | gulong nitems, bytes_after; 567 | guchar *tmp = NULL; 568 | gint result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 569 | *child_window, 570 | atom, 571 | 0, 572 | G_MAXLONG, 573 | False, 574 | utf8_string, 575 | &type, 576 | &format, 577 | &nitems, 578 | &bytes_after, 579 | &tmp); 580 | 581 | gchar *val = (gchar *) tmp; 582 | if (gdk_error_trap_pop() || result != Success || type != utf8_string) 583 | return NULL; 584 | 585 | if (format != 8 || nitems == 0) { 586 | if (val) 587 | XFree (val); 588 | return NULL; 589 | } 590 | 591 | if (!g_utf8_validate (val, nitems, NULL)) 592 | { 593 | XFree (val); 594 | return NULL; 595 | } 596 | 597 | gchar *retval = g_strndup (val, nitems); 598 | XFree (val); 599 | return retval; 600 | } 601 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | --------------------------------------------------------------------------------