From 18cf73cdf923950edb86d9cdc8c12e22a6e0625a Mon Sep 17 00:00:00 2001 From: Olivier Fourdan <fourdan.olivier@wanadoo.fr> Date: Thu, 6 Feb 2003 16:22:50 +0000 Subject: [PATCH] Add missing bits for full session management (Old svn revision: 10902) --- TODO | 14 +- aclocal.m4 | 2 + configure | 2 + src/Makefile.am | 2 + src/Makefile.in | 41 +++- src/client.c | 121 ++++++++---- src/client.h | 9 + src/events.c | 1 + src/hints.c | 163 ++++++++++++--- src/hints.h | 22 ++- src/main.c | 27 ++- src/session.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++++ src/session.h | 46 +++++ 13 files changed, 876 insertions(+), 91 deletions(-) create mode 100644 src/session.c create mode 100644 src/session.h diff --git a/TODO b/TODO index 079f0c60a..d4654f191 100644 --- a/TODO +++ b/TODO @@ -1,16 +1,6 @@ Things to do ============ -* Implement freedesktop.org standards ** DONE ** -* Use pango for font redering (aa text, multibytes, etc) ** DONE ** -* Add new buttons for menu and stick/unstick -* Implement window menu ** PARTIAL ** -* Implement xsettings -* Implement session management -* Reimplement Xinerama support -* Implement autoraise window using GTK timeout ** DONE ** -* Make transient windows stay stacked above their parents ** DONE ** -* Implement GTK event loop ** DONE ** -* Implement GTK menus ** DONE ** -* Bugfix on MapRequest (crash when mapping/unmapping quickly) ** DONE ** +Add your favorite wish list here : +* Multiscreen mode (not Xinerama) diff --git a/aclocal.m4 b/aclocal.m4 index 8554ec102..26657935b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -4655,6 +4655,8 @@ glib_DEFUN([GLIB_WITH_NLS], [CATOBJEXT=.mo DATADIRNAME=lib]) INSTOBJEXT=.mo + else + gt_cv_have_gettext=no fi fi ]) diff --git a/configure b/configure index 575f4c6a2..8b482e790 100755 --- a/configure +++ b/configure @@ -8814,6 +8814,8 @@ CATOBJEXT=.mo fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext INSTOBJEXT=.mo + else + gt_cv_have_gettext=no fi fi diff --git a/src/Makefile.am b/src/Makefile.am index e53847eda..46d232014 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,6 +21,8 @@ xfwm4_SOURCES = \ parserc.h \ pixmap.c \ pixmap.h \ + session.c \ + session.h \ settings.c \ settings.h \ tabwin.c \ diff --git a/src/Makefile.in b/src/Makefile.in index ae856f00a..b83968899 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -178,6 +178,8 @@ xfwm4_SOURCES = \ parserc.h \ pixmap.c \ pixmap.h \ + session.c \ + session.h \ settings.c \ settings.h \ tabwin.c \ @@ -207,8 +209,8 @@ am_xfwm4_OBJECTS = xfwm4-client.$(OBJEXT) xfwm4-events.$(OBJEXT) \ xfwm4-keyboard.$(OBJEXT) xfwm4-main.$(OBJEXT) \ xfwm4-menu.$(OBJEXT) xfwm4-misc.$(OBJEXT) \ xfwm4-parserc.$(OBJEXT) xfwm4-pixmap.$(OBJEXT) \ - xfwm4-settings.$(OBJEXT) xfwm4-tabwin.$(OBJEXT) \ - xfwm4-workspaces.$(OBJEXT) + xfwm4-session.$(OBJEXT) xfwm4-settings.$(OBJEXT) \ + xfwm4-tabwin.$(OBJEXT) xfwm4-workspaces.$(OBJEXT) xfwm4_OBJECTS = $(am_xfwm4_OBJECTS) xfwm4_DEPENDENCIES = xfwm4_LDFLAGS = @@ -225,6 +227,7 @@ am__depfiles_maybe = depfiles @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-misc.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-parserc.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-pixmap.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-session.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-settings.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-tabwin.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/xfwm4-workspaces.Po @@ -299,6 +302,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-misc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-parserc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-pixmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-settings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-tabwin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfwm4-workspaces.Po@am__quote@ @@ -669,6 +673,39 @@ xfwm4-pixmap.lo: pixmap.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -c -o xfwm4-pixmap.lo `test -f 'pixmap.c' || echo '$(srcdir)/'`pixmap.c +xfwm4-session.o: session.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -MT xfwm4-session.o -MD -MP -MF "$(DEPDIR)/xfwm4-session.Tpo" \ +@am__fastdepCC_TRUE@ -c -o xfwm4-session.o `test -f 'session.c' || echo '$(srcdir)/'`session.c; \ +@am__fastdepCC_TRUE@ then mv "$(DEPDIR)/xfwm4-session.Tpo" "$(DEPDIR)/xfwm4-session.Po"; \ +@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/xfwm4-session.Tpo"; exit 1; \ +@am__fastdepCC_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='session.c' object='xfwm4-session.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/xfwm4-session.Po' tmpdepfile='$(DEPDIR)/xfwm4-session.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -c -o xfwm4-session.o `test -f 'session.c' || echo '$(srcdir)/'`session.c + +xfwm4-session.obj: session.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -MT xfwm4-session.obj -MD -MP -MF "$(DEPDIR)/xfwm4-session.Tpo" \ +@am__fastdepCC_TRUE@ -c -o xfwm4-session.obj `if test -f 'session.c'; then $(CYGPATH_W) 'session.c'; else $(CYGPATH_W) '$(srcdir)/session.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv "$(DEPDIR)/xfwm4-session.Tpo" "$(DEPDIR)/xfwm4-session.Po"; \ +@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/xfwm4-session.Tpo"; exit 1; \ +@am__fastdepCC_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='session.c' object='xfwm4-session.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/xfwm4-session.Po' tmpdepfile='$(DEPDIR)/xfwm4-session.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -c -o xfwm4-session.obj `if test -f 'session.c'; then $(CYGPATH_W) 'session.c'; else $(CYGPATH_W) '$(srcdir)/session.c'; fi` + +xfwm4-session.lo: session.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -MT xfwm4-session.lo -MD -MP -MF "$(DEPDIR)/xfwm4-session.Tpo" \ +@am__fastdepCC_TRUE@ -c -o xfwm4-session.lo `test -f 'session.c' || echo '$(srcdir)/'`session.c; \ +@am__fastdepCC_TRUE@ then mv "$(DEPDIR)/xfwm4-session.Tpo" "$(DEPDIR)/xfwm4-session.Plo"; \ +@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/xfwm4-session.Tpo"; exit 1; \ +@am__fastdepCC_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='session.c' object='xfwm4-session.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/xfwm4-session.Plo' tmpdepfile='$(DEPDIR)/xfwm4-session.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -c -o xfwm4-session.lo `test -f 'session.c' || echo '$(srcdir)/'`session.c + xfwm4-settings.o: settings.c @am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xfwm4_CFLAGS) $(CFLAGS) -MT xfwm4-settings.o -MD -MP -MF "$(DEPDIR)/xfwm4-settings.Tpo" \ @am__fastdepCC_TRUE@ -c -o xfwm4-settings.o `test -f 'settings.c' || echo '$(srcdir)/'`settings.c; \ diff --git a/src/client.c b/src/client.c index 04b49bb56..f634e9f10 100644 --- a/src/client.c +++ b/src/client.c @@ -35,6 +35,7 @@ #include "workspaces.h" #include "settings.h" #include "tabwin.h" +#include "session.h" #include "debug.h" #include "my_intl.h" @@ -697,7 +698,10 @@ static void clientGetInitialNetWmDesktop(Client * c) DBG("entering clientGetInitialNetWmDesktop\n"); DBG("client \"%s\" (%#lx)\n", c->name, c->window); - c->win_workspace = workspace; + if (!CLIENT_FLAG_TEST(c, CLIENT_FLAG_SESSION_MANAGED)) + { + c->win_workspace = workspace; + } if(getNetHint(dpy, c->window, net_wm_desktop, &val)) { DBG("atom net_wm_desktop detected\n"); @@ -1838,6 +1842,53 @@ void clientUpdateMWMHints(Client * c) } } +static inline void clientFree(Client *c) +{ + g_return_if_fail (c != NULL); + + DBG("entering clientFree\n"); + DBG("freeing client \"%s\" (%#lx)\n", c->name, c->window); + + if(c->name) + { + free(c->name); + } + if(c->size) + { + XFree(c->size); + } + if(c->wmhints) + { + XFree(c->wmhints); + } + if(c->ncmap > 0) + { + XFree(c->cmap_windows); + } + if(c->class.res_name) + { + XFree(c->class.res_name); + } + if(c->class.res_class) + { + XFree(c->class.res_class); + } + if(c->client_id) + { + XFree(c->client_id); + } + if(c->window_role) + { + XFree(c->window_role); + } + if((c->wm_command) && (c->wm_command_count)) + { + XFreeStringList(c->wm_command); + } + + free(c); +} + void clientFrame(Window w) { XWindowAttributes attr; @@ -1904,8 +1955,23 @@ void clientFrame(Window w) c->ncmap = 0; } + c->class.res_name = NULL; + c->class.res_class = NULL; + XGetClassHint (dpy, w, &c->class); c->wmhints = XGetWMHints(dpy, c->window); - + getClientID(dpy, c->window, &(c->client_id)); + c->client_leader = getClientLeader(dpy, c->window); + if (c->client_leader != None) + { + getWindowRole(dpy, c->client_leader, &(c->window_role)); + } + else + { + c->window_role = NULL; + } + c->wm_command_count = 0; + getWindowCommand(dpy, c->window, &(c->wm_command), &(c->wm_command_count)); + /* Initialize structure */ c->client_flag = (CLIENT_FLAG_HAS_BORDER | CLIENT_FLAG_HAS_MENU | CLIENT_FLAG_HAS_MAXIMIZE | CLIENT_FLAG_HAS_HIDE | CLIENT_FLAG_HAS_CLOSE | CLIENT_FLAG_HAS_MOVE | CLIENT_FLAG_HAS_RESIZE); @@ -1931,6 +1997,12 @@ void clientFrame(Window w) c->win_layer = WIN_LAYER_NORMAL; } + /* Reload from session */ + if (sessionMatchWinToSM (c)) + { + CLIENT_FLAG_SET(c, CLIENT_FLAG_SESSION_MANAGED); + } + CLIENT_FLAG_SET(c, (CLIENT_FLAG_TEST(c, CLIENT_FLAG_STICKY)) ? CLIENT_FLAG_STICKY : 0); CLIENT_FLAG_SET(c, (CLIENT_FLAG_TEST(c, CLIENT_FLAG_SHADED)) ? CLIENT_FLAG_SHADED : 0); CLIENT_FLAG_SET(c, (c->win_state & (WIN_STATE_MAXIMIZED_VERT | WIN_STATE_MAXIMIZED)) ? CLIENT_FLAG_MAXIMIZED_VERT : 0); @@ -1960,27 +2032,21 @@ void clientFrame(Window w) } /* Once we know the type of window, we can initialize window position */ - if(attr.map_state != IsViewable) + if(!CLIENT_FLAG_TEST(c, CLIENT_FLAG_SESSION_MANAGED)) { - clientInitPosition(c); - } - else - { - clientGravitate(c, APPLY); + if (attr.map_state != IsViewable) + { + clientInitPosition(c); + } + else + { + clientGravitate(c, APPLY); + } } - gdk_x11_grab_server(); if(XGetGeometry(dpy, w, &dummy_root, &dummy_x, &dummy_y, &dummy_width, &dummy_height, &dummy_bw, &dummy_depth) == 0) { - if(c->name) - { - free(c->name); - } - if(c->size) - { - XFree(c->size); - } - free(c); + clientFree(c); gdk_x11_ungrab_server(); return; } @@ -2084,24 +2150,7 @@ void clientUnframe(Client * c, int remap) { workspaceUpdateArea(margins, gnome_margins); } - if(c->name) - { - free(c->name); - } - if(c->size) - { - XFree(c->size); - } - if(c->wmhints) - { - XFree(c->wmhints); - } - if(c->ncmap > 0) - { - XFree(c->cmap_windows); - } - free(c); - DBG("client_count=%d\n", client_count); + clientFree(c); } void clientFrameAll() diff --git a/src/client.h b/src/client.h index b1d575843..3746d0b93 100644 --- a/src/client.h +++ b/src/client.h @@ -106,6 +106,8 @@ #define CLIENT_FLAG_WM_TAKEFOCUS (1L<<25) #define CLIENT_FLAG_RESIZING (1L<<26) #define CLIENT_FLAG_MOVING (1L<<27) +#define CLIENT_FLAG_NAME_CHANGED (1L<<28) +#define CLIENT_FLAG_SESSION_MANAGED (1L<<29) /* Convenient macros */ #define CLIENT_FLAG_TEST(c,f) (c->client_flag & (f)) @@ -145,14 +147,17 @@ struct _Client Window sides[3]; Window corners[4]; Window buttons[BUTTON_COUNT]; + Window client_leader; Colormap cmap; unsigned long win_hints; unsigned long win_state; unsigned long win_layer; + int win_workspace; Atom type_atom; XSizeHints *size; XWMHints *wmhints; + XClassHint class; Client *next; Client *prev; WindowType type; @@ -176,6 +181,10 @@ struct _Client int ncmap; int button_pressed[BUTTON_COUNT]; int struts[4]; + char *client_id; + char *window_role; + int wm_command_count; + char **wm_command; char *name; unsigned long client_flag; }; diff --git a/src/events.c b/src/events.c index 7be178099..259b26344 100644 --- a/src/events.c +++ b/src/events.c @@ -809,6 +809,7 @@ static inline void handlePropertyNotify(XPropertyEvent * ev) free(c->name); } getWindowName(dpy, c->window, &c->name); + CLIENT_FLAG_SET(c, CLIENT_FLAG_NAME_CHANGED); frameDraw(c); } else if(ev->atom == motif_wm_hints) diff --git a/src/hints.c b/src/hints.c index dfbfb2a00..62149fce1 100644 --- a/src/hints.c +++ b/src/hints.c @@ -140,7 +140,7 @@ unsigned long getWMState(Display * dpy, Window w) state = *data; XFree(data); } - return (state); + return state; } void setWMState(Display * dpy, Window w, unsigned long state) @@ -173,11 +173,11 @@ PropMwmHints *getMotifHints(Display * dpy, Window w) if((XGetWindowProperty(dpy, w, motif_wm_hints, 0L, 20L, False, motif_wm_hints, &real_type, &real_format, &items_read, &items_left, (unsigned char **)&data) == Success) && (items_read)) { - return (data); + return data; } else { - return (NULL); + return NULL; } } @@ -227,7 +227,7 @@ unsigned int getWMProtocols(Display * dpy, Window w) { XFree(protocols); } - return (result); + return result; } @@ -247,10 +247,11 @@ void initGnomeHints(Display * dpy) win_workspace = XInternAtom(dpy, "_WIN_WORKSPACE", False); } -int getGnomeHint(Display * dpy, Window w, Atom a, long *value) +gboolean getGnomeHint(Display * dpy, Window w, Atom a, long *value) { Atom real_type; - int real_format, success = False; + int real_format; + gboolean success = FALSE; unsigned long items_read, items_left; long *data = NULL; @@ -262,9 +263,9 @@ int getGnomeHint(Display * dpy, Window w, Atom a, long *value) { *value = *data; XFree(data); - success = True; + success = TRUE; } - return (success); + return success; } void setGnomeHint(Display * dpy, Window w, Atom a, long value) @@ -365,10 +366,11 @@ void initNetHints(Display * dpy) utf8_string = XInternAtom(dpy, "UTF8_STRING", False); } -int getNetHint(Display * dpy, Window w, Atom a, long *value) +gboolean getNetHint(Display * dpy, Window w, Atom a, long *value) { Atom real_type; - int real_format, success = False; + int real_format; + gboolean success = FALSE; unsigned long items_read, items_left; long *data = NULL; @@ -380,9 +382,9 @@ int getNetHint(Display * dpy, Window w, Atom a, long *value) { *value = *data; XFree(data); - success = True; + success = TRUE; } - return (success); + return success; } void set_net_supported_hint(Display * dpy, int screen, Window check_win) @@ -450,16 +452,16 @@ void set_net_supported_hint(Display * dpy, int screen, Window check_win) XChangeProperty(dpy, RootWindow(dpy, screen), net_supporting_wm_check, XA_WINDOW, 32, PropModeReplace, (unsigned char *)data, 1); } -static int check_type_and_format(Display * dpy, Window w, Atom a, int expected_format, Atom expected_type, int n_items, int format, Atom type) +static gboolean check_type_and_format(Display * dpy, Window w, Atom a, int expected_format, Atom expected_type, int n_items, int format, Atom type) { if((expected_format == format) && (expected_type == type) && (n_items < 0 || n_items > 0)) { - return (True); + return TRUE; } - return (False); + return FALSE; } -int get_atom_list(Display * dpy, Window w, Atom a, Atom ** atoms_p, int *n_atoms_p) +gboolean get_atom_list(Display * dpy, Window w, Atom a, Atom ** atoms_p, int *n_atoms_p) { Atom type; int format; @@ -472,7 +474,7 @@ int get_atom_list(Display * dpy, Window w, Atom a, Atom ** atoms_p, int *n_atoms if((XGetWindowProperty(dpy, w, a, 0, G_MAXLONG, False, XA_ATOM, &type, &format, &n_atoms, &bytes_after, (unsigned char **)&atoms) != Success) || (type == None)) { - return (False); + return FALSE; } if(!check_type_and_format(dpy, w, a, 32, XA_ATOM, -1, format, type)) @@ -483,16 +485,16 @@ int get_atom_list(Display * dpy, Window w, Atom a, Atom ** atoms_p, int *n_atoms } *atoms_p = NULL; *n_atoms_p = 0; - return (False); + return FALSE; } *atoms_p = atoms; *n_atoms_p = n_atoms; - return (True); + return TRUE; } -int get_cardinal_list(Display * dpy, Window w, Atom xatom, unsigned long **cardinals_p, int *n_cardinals_p) +gboolean get_cardinal_list(Display * dpy, Window w, Atom xatom, unsigned long **cardinals_p, int *n_cardinals_p) { Atom type; int format; @@ -505,19 +507,19 @@ int get_cardinal_list(Display * dpy, Window w, Atom xatom, unsigned long **cardi if((XGetWindowProperty(dpy, w, xatom, 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &n_cardinals, &bytes_after, (unsigned char **)&cardinals) != Success) || (type == None)) { - return False; + return FALSE; } if(!check_type_and_format(dpy, w, xatom, 32, XA_CARDINAL, -1, format, type)) { XFree(cardinals); - return False; + return FALSE; } *cardinals_p = cardinals; *n_cardinals_p = n_cardinals; - return True; + return TRUE; } void set_net_workarea(Display * dpy, int screen, int nb_workspaces, CARD32 * margins) @@ -579,7 +581,7 @@ void getTransientFor(Display * dpy, Window w, Window * transient_for) DBG("Window (%#lx) is transient for (%#lx)\n", w, *transient_for); } -int get_utf8_string(Display * dpy, Window w, Atom xatom, char **str_p) +gboolean get_utf8_string(Display * dpy, Window w, Atom xatom, char **str_p) { Atom type; int format; @@ -593,7 +595,7 @@ int get_utf8_string(Display * dpy, Window w, Atom xatom, char **str_p) if((XGetWindowProperty(dpy, w, xatom, 0, G_MAXLONG, False, utf8_string, &type, &format, &n_items, &bytes_after, (unsigned char **)&str) != Success) || (type == None)) { DBG("no utf8_string value provided\n"); - return False; + return FALSE; } if(!check_type_and_format(dpy, w, xatom, 8, utf8_string, -1, format, type)) @@ -603,7 +605,7 @@ int get_utf8_string(Display * dpy, Window w, Atom xatom, char **str_p) { XFree(str); } - return False; + return FALSE; } if(!g_utf8_validate(str, n_items, NULL)) @@ -618,12 +620,12 @@ int get_utf8_string(Display * dpy, Window w, Atom xatom, char **str_p) } XFree(str); - return False; + return FALSE; } *str_p = str; - return True; + return TRUE; } static char *text_property_to_utf8(Display * dpy, const XTextProperty * prop) @@ -675,9 +677,13 @@ static char *get_text_property(Display * dpy, Window w, Atom a) void getWindowName(Display * dpy, Window w, char **name) { char *str; - + DBG("entering getWindowName\n"); + + g_return_if_fail (name != NULL); *name = NULL; + g_return_if_fail (w != None); + if(get_utf8_string(dpy, w, net_wm_name, &str)) { *name = strdup(str); @@ -694,6 +700,103 @@ void getWindowName(Display * dpy, Window w, char **name) { *name = strdup(""); } +} + +gboolean getWindowRole(Display * dpy, Window window, char **role) +{ + XTextProperty tp; + + DBG("entering GetWindowRole\n"); + + g_return_val_if_fail (role != NULL, FALSE); + *role = NULL; + g_return_val_if_fail (window != None, FALSE); + + if(XGetTextProperty(dpy, window, &tp, wm_window_role)) + { + if((tp.value) && (tp.encoding == XA_STRING) && (tp.format == 8) && (tp.nitems != 0)) + { + *role = strdup((char *)tp.value); + XFree(tp.value); + return TRUE; + } + } + + return FALSE; +} + +Window getClientLeader(Display * dpy, Window window) +{ + Window client_leader = None; + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop = NULL; + + DBG("entering getClientLeader\n"); + + g_return_val_if_fail (window != None, None); + + if(XGetWindowProperty(dpy, window, wm_client_leader, 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success) + { + if((prop) && (actual_type == XA_WINDOW) && (actual_format == 32) && (nitems == 1) && (bytes_after == 0)) + { + client_leader = *((Window *) prop); + } + if(prop) + { + XFree(prop); + } + } + return client_leader; +} + +gboolean getClientID(Display * dpy, Window window, char **client_id) +{ + Window id; + XTextProperty tp; + + DBG("entering getClientID\n"); + + g_return_val_if_fail (client_id != NULL, FALSE); + *client_id = NULL; + g_return_val_if_fail (window != None, FALSE); + + if((id = getClientLeader(dpy, window))) + { + if(XGetTextProperty(dpy, id, &tp, sm_client_id)) + { + if(tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) + { + *client_id = strdup((char *)tp.value); + XFree(tp.value); + return TRUE; + } + } + } - return; + return FALSE; } + +gboolean getWindowCommand(Display * dpy, Window window, char ***argv, int *argc) +{ + Window id; + + *argc = 0; + g_return_val_if_fail(window != None, FALSE); + + if(XGetCommand(dpy, window, argv, argc) && (*argc > 0)) + { + return TRUE; + } + if((id = getClientLeader(dpy, window))) + { + if(XGetCommand(dpy, id, argv, argc) && (*argc > 0)) + { + return TRUE; + } + } + return FALSE; +} + diff --git a/src/hints.h b/src/hints.h index c118639fc..a6cce22ec 100644 --- a/src/hints.h +++ b/src/hints.h @@ -19,14 +19,15 @@ */ -#ifndef __HINTS_H -#define __HINTS_H +#ifndef __HINTS_H__ +#define __HINTS_H__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <X11/Xatom.h> +#include <glib.h> #define MWM_HINTS_FUNCTIONS (1L << 0) #define MWM_HINTS_DECORATIONS (1L << 1) @@ -181,23 +182,28 @@ void initMotifHints(Display *); PropMwmHints *getMotifHints(Display *, Window); unsigned int getWMProtocols(Display *, Window); void initGnomeHints(Display *); -int getGnomeHint(Display *, Window, Atom, long *); +gboolean getGnomeHint(Display *, Window, Atom, long *); void setGnomeHint(Display *, Window, Atom, long); void getGnomeDesktopMargins(Display *, int, CARD32 *); void setGnomeProtocols(Display *, int, Window); void initNetHints(Display * dpy); -int getNetHint(Display *, Window, Atom, long *); +gboolean getNetHint(Display *, Window, Atom, long *); void set_net_supported_hint(Display *, int, Window); -int get_atom_list(Display *, Window, Atom, Atom **, int *); -int get_cardinal_list(Display *, Window, Atom, unsigned long **, int *); +gboolean get_atom_list(Display *, Window, Atom, Atom **, int *); +gboolean get_cardinal_list(Display *, Window, Atom, unsigned long **, int *); void set_net_workarea(Display *, int, int, CARD32 *); void init_net_desktop_params(Display *, int, int); void set_utf8_string_hint(Display *, Window, Atom, const char *); void getTransientFor(Display * dpy, Window w, Window * transient_for); void getWindowName(Display * dpy, Window w, char **name); -int get_utf8_string(Display *, Window, Atom, char **); +gboolean get_utf8_string(Display *, Window, Atom, char **); void getWindowName(Display *, Window, char **); +gboolean getWindowRole(Display *, Window, char **); +Window getClientLeader(Display *, Window); +gboolean getClientID(Display *, Window, char **); +gboolean getWindowCommand(Display *, Window, char ***, int *); + #define setNetHint setGnomeHint -#endif /* __HINTS_H */ +#endif /* __HINTS_H__ */ diff --git a/src/main.c b/src/main.c index bb16ded67..5f48bc97b 100644 --- a/src/main.c +++ b/src/main.c @@ -47,6 +47,7 @@ #include "menu.h" #include "keyboard.h" #include "workspaces.h" +#include "session.h" #include "debug.h" #include "my_intl.h" @@ -106,6 +107,7 @@ static void cleanUp() XFreeCursor(dpy, root_cursor); XFreeCursor(dpy, move_cursor); xineramaFree(); + sessionFreeWindowStates(); for(i = 0; i < 7; i++) { XFreeCursor(dpy, resize_cursor[i]); @@ -122,9 +124,24 @@ static void cleanUp() closeEventFilter(); } -static void session_save_phase_2(gpointer client_data) +static void load_saved_session(void) { - g_print("TODO: Save session\n"); + const gchar *home = g_getenv("HOME"); + gchar *filename; + + filename = g_build_filename(home, G_DIR_SEPARATOR_S, ".xfwm4-session", NULL); + sessionLoadWindowStates(filename); + g_free(filename); +} + +static void save_phase_2(gpointer data) +{ + const gchar *home = g_getenv("HOME"); + gchar *filename; + + filename = g_build_filename(home, G_DIR_SEPARATOR_S, ".xfwm4-session", NULL); + sessionSaveWindowStates(filename); + g_free(filename); } static void session_die(gpointer client_data) @@ -194,13 +211,17 @@ static int initialize(int argc, char **argv) xinerama_heads = xineramaGetHeads(); client_session = client_session_new(argc, argv, NULL, SESSION_RESTART_IF_RUNNING, 20); - client_session->save_phase_2 = session_save_phase_2; + client_session->save_phase_2 = save_phase_2; client_session->die = session_die; if(!session_init(client_session)) { g_message(_("%s: Running without session manager"), g_get_prgname()); } + else + { + load_saved_session(); + } margins[MARGIN_TOP] = gnome_margins[MARGIN_TOP] = 0; margins[MARGIN_LEFT] = gnome_margins[MARGIN_LEFT] = 0; diff --git a/src/session.c b/src/session.c new file mode 100644 index 000000000..9050b456f --- /dev/null +++ b/src/session.c @@ -0,0 +1,517 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Initially inspired by xfwm, fvwm2, enlightment and twm implementations */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pwd.h> +#include <unistd.h> +#include <stdarg.h> +#include <sys/types.h> +#include <signal.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <glib.h> + +#include "session.h" +#include "main.h" +#include "hints.h" +#include "client.h" + +typedef struct _match +{ + unsigned long win; + unsigned long client_leader; + char *client_id; + char *res_name; + char *res_class; + char *window_role; + char *wm_name; + int wm_command_count; + char **wm_command; + int x, y, w, h; + int desktop; + unsigned long flags; + gboolean used; +} +Match; + +static int num_match = 0; +static Match *matches = NULL; + +/* + 2-pass function to compute new string length, + allocate memory and finally copy string + - Returned value must be freed - + */ +static gchar *escape_quote(gchar * s) +{ + gchar *ns; + gchar *idx1, *idx2; + gint nbquotes = 0; + gint lg = 0; + + g_return_val_if_fail(s != NULL, NULL); + + lg = strlen(s); + /* First, count quotes in string */ + idx1 = s; + while(*idx1) + { + if(*(idx1++) == '"') + nbquotes++; + } + /* If there is no quote in the string, return it */ + if(!nbquotes) + return (g_strdup(s)); + + /* Or else, allocate memory for the new string */ + ns = g_new(gchar, lg + nbquotes + 1); + /* And prepend a backslash before any quote found in string */ + idx1 = s; + idx2 = ns; + while(*idx1) + { + if(*idx1 == '"') + { + *(idx2++) = '\\'; + *(idx2++) = '"'; + } + else + { + *(idx2++) = *idx1; + } + idx1++; + } + /* Add null char */ + *idx2 = '\0'; + return ns; +} + +/* + single-pass function to replace backslash+quotes + by quotes. + - Returned value must be freed - + */ +static gchar *unescape_quote(gchar * s) +{ + gchar *ns; + gboolean backslash; + gchar *idx1, *idx2; + gint lg; + + g_return_val_if_fail(s != NULL, NULL); + + lg = strlen(s); + backslash = FALSE; + ns = g_new(gchar, lg + 1); + idx1 = s; + idx2 = ns; + while(*idx1) + { + if(*idx1 == '\\') + { + *(idx2++) = *idx1; + backslash = TRUE; + } + else if((*idx1 == '"') && backslash) + { + /* Move backward to override the "\" */ + *(--idx2) = *idx1; + idx2++; + backslash = FALSE; + } + else + { + *(idx2++) = *idx1; + backslash = FALSE; + } + idx1++; + } + *idx2 = '\0'; + return ns; +} + +static gchar *getsubstring(gchar * s, gint * length) +{ + gchar pbrk; + gchar *ns; + gchar *end, *idx1, *idx2, *skip; + gint lg; + gboolean finished = FALSE, backslash = FALSE; + + lg = *length = 0; + g_return_val_if_fail(s != NULL, NULL); + + end = skip = s; + while((*skip == ' ') || (*skip == '\t')) + { + end = ++skip; + (*length)++; + } + if(*skip == '"') + { + pbrk = '"'; + end = ++skip; + (*length)++; + } + else + { + pbrk = ' '; + } + + finished = FALSE; + while((!finished) && (*end)) + { + if(*end == '\\') + { + backslash = TRUE; + } + else if((*end == pbrk) && backslash) + { + backslash = FALSE; + } + else if(*end == pbrk) + { + finished = TRUE; + } + end++; + lg++; + (*length)++; + } + ns = g_new(gchar, lg + 1); + /* Skip pbrk character */ + end--; + idx1 = skip; + idx2 = ns; + do + { + *(idx2++) = *idx1; + } + while(++idx1 < end); + *idx2 = '\0'; + return ns; +} + +gboolean sessionSaveWindowStates(gchar *filename) +{ + FILE *f; + Client *c; + gint client_idx; + + g_return_val_if_fail(filename != NULL, FALSE); + + if((f = fopen(filename, "w"))) + { + for(c = clients, client_idx = 0; client_idx < client_count; c = c->next, client_idx++) + { + fprintf(f, "[CLIENT] 0x%lx\n", c->window); + + if(c->client_id) + { + fprintf(f, " [CLIENT_ID] %s\n", c->client_id); + } + + if(c->client_leader) + { + fprintf(f, " [CLIENT_LEADER] 0x%lx\n", c->client_leader); + } + + if(c->window_role) + { + fprintf(f, " [WINDOW_ROLE] %s\n", c->window_role); + } + + if(c->class.res_class) + { + fprintf(f, " [RES_NAME] %s\n", c->class.res_name); + } + + if(c->class.res_name) + { + fprintf(f, " [RES_CLASS] %s\n", c->class.res_class); + } + + if(c->name) + { + fprintf(f, " [WM_NAME] %s\n", c->name); + } + + if(c->wm_command_count > 0) + { + gint j; + fprintf(f, " [WM_COMMAND] (%i)", c->wm_command_count); + for(j = 0; j < c->wm_command_count; j++) + { + gchar *escaped_string; + escaped_string = escape_quote(c->wm_command[j]); + fprintf(f, " \"%s\"", escaped_string); + g_free(escaped_string); + } + fprintf(f, "\n"); + } + + fprintf(f, " [GEOMETRY] (%i,%i,%i,%i)\n", c->x, c->y, c->width, c->height); + fprintf(f, " [DESK] %i\n", c->win_workspace); + fprintf(f, " [FLAGS] 0x%lx\n", CLIENT_FLAG_TEST(c, CLIENT_FLAG_STICKY | CLIENT_FLAG_HIDDEN | CLIENT_FLAG_SHADED | CLIENT_FLAG_NAME_CHANGED)); + } + fclose(f); + return TRUE; + } + return FALSE; +} + +gboolean sessionLoadWindowStates(gchar *filename) +{ + FILE *f; + char s[4096], s1[4096]; + int i, pos, pos1; + unsigned long w; + + g_return_val_if_fail(filename != NULL, FALSE); + if((f = fopen(filename, "r"))) + { + while(fgets(s, sizeof(s), f)) + { + sscanf(s, "%4000s", s1); + if(!strcmp(s1, "[CLIENT]")) + { + sscanf(s, "%*s %lx", &w); + num_match++; + matches = realloc(matches, sizeof(Match) * num_match); + matches[num_match - 1].win = w; + matches[num_match - 1].client_id = NULL; + matches[num_match - 1].res_name = NULL; + matches[num_match - 1].res_class = NULL; + matches[num_match - 1].window_role = NULL; + matches[num_match - 1].wm_name = NULL; + matches[num_match - 1].wm_command_count = 0; + matches[num_match - 1].wm_command = NULL; + matches[num_match - 1].x = 0; + matches[num_match - 1].y = 0; + matches[num_match - 1].w = 100; + matches[num_match - 1].h = 100; + matches[num_match - 1].desktop = 0; + matches[num_match - 1].used = FALSE; + matches[num_match - 1].flags = 0; + } + else if(!strcmp(s1, "[GEOMETRY]")) + { + sscanf(s, "%*s (%i,%i,%i,%i)", &(matches[num_match - 1].x), &(matches[num_match - 1].y), &(matches[num_match - 1].w), &(matches[num_match - 1].h)); + } + else if(!strcmp(s1, "[DESK]")) + { + sscanf(s, "%*s %i", &(matches[num_match - 1].desktop)); + } + else if(!strcmp(s1, "[CLIENT_LEADER]")) + { + sscanf(s, "%*s 0x%lx", &(matches[num_match - 1].client_leader)); + } + else if(!strcmp(s1, "[FLAGS]")) + { + sscanf(s, "%*s 0x%lx", &(matches[num_match - 1].flags)); + } + else if(!strcmp(s1, "[CLIENT_ID]")) + { + sscanf(s, "%*s %[^\n]", s1); + matches[num_match - 1].client_id = strdup(s1); + } + else if(!strcmp(s1, "[WINDOW_ROLE]")) + { + sscanf(s, "%*s %[^\n]", s1); + matches[num_match - 1].window_role = strdup(s1); + } + else if(!strcmp(s1, "[RES_NAME]")) + { + sscanf(s, "%*s %[^\n]", s1); + matches[num_match - 1].res_name = strdup(s1); + } + else if(!strcmp(s1, "[RES_CLASS]")) + { + sscanf(s, "%*s %[^\n]", s1); + matches[num_match - 1].res_class = strdup(s1); + } + else if(!strcmp(s1, "[WM_NAME]")) + { + sscanf(s, "%*s %[^\n]", s1); + matches[num_match - 1].wm_name = strdup(s1); + } + else if(!strcmp(s1, "[WM_COMMAND]")) + { + sscanf(s, "%*s (%i)%n", &matches[num_match - 1].wm_command_count, &pos); + matches[num_match - 1].wm_command = (char **) malloc (sizeof (char *) * (matches[num_match - 1].wm_command_count + 1)); + for(i = 0; i < matches[num_match - 1].wm_command_count; i++) + { + gchar *substring; + substring = getsubstring(s + pos, &pos1); + pos += pos1; + matches[num_match - 1].wm_command[i] = unescape_quote(substring); + g_free(substring); + } + matches[num_match - 1].wm_command[matches[num_match - 1].wm_command_count] = NULL; + } + + } + fclose(f); + return TRUE; + } + return FALSE; +} + +void sessionFreeWindowStates(void) +{ + int i; + for(i = 0; i < num_match; i++) + { + if(matches[i].client_id) + { + free(matches[i].client_id); + matches[i].client_id = NULL; + } + if(matches[i].res_name) + { + free(matches[i].res_name); + matches[i].res_name = NULL; + } + if(matches[i].res_class) + { + free(matches[i].res_class); + matches[i].res_class = NULL; + } + if(matches[i].window_role) + { + free(matches[i].window_role); + matches[i].window_role = NULL; + } + if(matches[i].wm_name) + { + free(matches[i].wm_name); + matches[i].wm_name = NULL; + } + if((matches[i].wm_command_count) && (matches[i].wm_command)) + { + XFreeStringList(matches[i].wm_command); + matches[i].wm_command_count = 0; + matches[i].wm_command = NULL; + } + } + if (matches) + { + free(matches); + matches = NULL; + num_match = 0; + } +} + +/* This complicated logic is from twm, where it is explained */ + +#define xstreq(a,b) ((!a && !b) || (a && b && (strcmp(a,b)==0))) + +static gboolean matchWin(Client * c, Match * m) +{ + gboolean found; + int i; + + g_return_val_if_fail (c != NULL, FALSE); + + found = FALSE; + if(xstreq(c->client_id, m->client_id)) + { + /* client_id's match */ + if((c->window_role) || (m->window_role)) + { + /* We have or had a window role, base decision on it */ + found = xstreq(c->window_role, m->window_role); + } + else + { + /* Compare res_class, res_name and WM_NAME, unless the + * WM_NAME has changed + */ + if(xstreq(c->class.res_name, m->res_name) && (CLIENT_FLAG_TEST(c, CLIENT_FLAG_NAME_CHANGED) || (m->flags & CLIENT_FLAG_NAME_CHANGED) || xstreq(c->name, m->wm_name))) + { + if(c->client_id) + { + /* If we have a client_id, we don't compare + WM_COMMAND, since it will be different. */ + found = 1; + } + else + { + /* for non-SM-aware clients we also compare WM_COMMAND */ + if(c->wm_command_count == m->wm_command_count) + { + for(i = 0; i < c->wm_command_count; i++) + { + if(strcmp(c->wm_command[i], m->wm_command[i]) != 0) + break; + } + + if((i == c->wm_command_count) && (c->wm_command_count)) + { + found = TRUE; + } + } /* if (wm_command_count ==... */ + /* We have to deal with a now-SM-aware client, it means that it won't probably + * restore its state in a proper manner. + * Thus, we also mark all other instances of this application as used, to avoid + * dummy side effects in case we found a matching entry. + */ + if(found) + { + for(i = 0; i < num_match; i++) + { + if(!(matches[i].used) && !(&matches[i] == m) && (m->client_leader) && (matches[i].client_leader == m->client_leader)) + { + matches[i].used = TRUE; + } + } + } + } + } + } + } + + return found; +} + +gboolean sessionMatchWinToSM(Client *c) +{ + int i; + + g_return_if_fail (c != NULL); + for(i = 0; i < num_match; i++) + { + if(!matches[i].used && matchWin(c, &matches[i])) + { + matches[i].used = TRUE; + c->x = matches[i].x; + c->y = matches[i].y; + c->width = matches[i].w; + c->height = matches[i].h; + c->win_workspace = matches[i].desktop; + CLIENT_FLAG_SET(c, matches[i].flags & (CLIENT_FLAG_STICKY | CLIENT_FLAG_SHADED | CLIENT_FLAG_HIDDEN)); + return TRUE; + } + } + return FALSE; +} diff --git a/src/session.h b/src/session.h new file mode 100644 index 000000000..77b834771 --- /dev/null +++ b/src/session.h @@ -0,0 +1,46 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + xfwm4 - (c) 2003 Olivier Fourdan + + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#include <glib.h> +#include "client.h" + +/* +** Save window states to file which name is given in argument. +*/ +gboolean sessionSaveWindowStates(gchar *); + +/* +** Load window states to file which name is given in argument. +*/ +gboolean sessionLoadWindowStates(gchar *); + +/* +** Free allocated structure. Should be called before xfwm4 dies +*/ +void sessionFreeWindowStates(void); + +/* +** Search for existing client in saved session and update +** relevant client fields if found. +*/ +gboolean sessionMatchWinToSM(Client *); + +#endif /* __CLIENT_H__ */ -- GitLab