[PATCH] Built-in workspaces

Repolho <[email protected]>
Newsgroups gmane.comp.window-managers.ratpoison.devel
Message-ID <20131028200705.GB31685@darch>
Here's a proposed implementation of built-in workspaces that keeps the
group system intact from the user's point of view. It performs
noticeably better under heavy CPU or disk usage than external solutions.

It works by storing an sfdump into the current group before a group
change, and then allowing for the user to restore those dumps when he
goes back to that group. This is accomplished by making available two
new user commands: grestore and gclean.

grestore will restore the sfdump stored in a group (defaulting to the
current one). gclean will empty any frames whose windows don't belong to
a group (also defaulting to the current one).

By adding both these commands to the switchgroup hook, the user will get
built-in workspaces, where switching to another group restores the
screens' state to the last state they were in when that group was last
used. By not using the commands at all, the user will get the usual
group behavior. The only difference for the latter is the extra memory
usage of storing an sfdump per group, and the extra CPU usage of an
sfdump() call per group change, both of which are insignificant and
unnoticeable.

Additionally, the patch also adds a new hook, movetogroup, to be
triggered when a window is moved to another group. This is so that the
user can add gclean to said hook, so the window will disappear from the
screen after being moved. Again, a user that chooses not to do anything
will experience the same group behavior as always.


A sample configuration file is also attached to ease testing.

I look forward to hearing your opinions on the idea as well as the
implementation.

_______________________________________________
Ratpoison-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/ratpoison-devel
ratpoison-builtin-workspaces.patch (text/x-diff, 10.2 KB)
diff --git a/src/actions.c b/src/actions.c
index 2369067..2db049c 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -249,6 +249,8 @@ init_user_commands(void)
                "Frames: ", arg_REST);
   add_command ("fselect",       cmd_fselect,    1, 1, 1,
                "", arg_FRAME);
+  add_command ("gclean",        cmd_gclean,     1, 0, 0,
+               "Group: ", arg_GROUP);
   add_command ("gdelete",       cmd_gdelete,    1, 0, 0,
                "Group:", arg_GROUP);
   add_command ("getenv",        cmd_getenv,     1, 1, 1,
@@ -266,6 +268,8 @@ init_user_commands(void)
                "Number: ", arg_NUMBER);
   add_command ("grename",       cmd_grename,    1, 1, 1,
                "Change group name to: ", arg_REST);
+  add_command ("grestore",       cmd_grestore,  1, 0, 0,
+               "Group: ", arg_GROUP);
   add_command ("gnext",         cmd_gnext,      0, 0, 0);
   add_command ("gprev",         cmd_gprev,      0, 0, 0);
   add_command ("gother",        cmd_gother,     0, 0, 0);
@@ -5043,6 +5047,7 @@ set_winliststyle (struct cmdarg **args)
 cmdret *
 cmd_gnext (int interactive UNUSED, struct cmdarg **args UNUSED)
 {
+  store_current_group_frames (sfdump());
   set_current_group (group_next_group ());
   return cmdret_new (RET_SUCCESS, NULL);
 }
@@ -5050,6 +5055,7 @@ cmd_gnext (int interactive UNUSED, struct cmdarg **args UNUSED)
 cmdret *
 cmd_gprev (int interactive UNUSED, struct cmdarg **args UNUSED)
 {
+  store_current_group_frames (sfdump());
   set_current_group (group_prev_group ());
   return cmdret_new (RET_SUCCESS, NULL);
 }
@@ -5057,6 +5063,7 @@ cmd_gprev (int interactive UNUSED, struct cmdarg **args UNUSED)
 cmdret *
 cmd_gother (int interactive UNUSED, struct cmdarg **args UNUSED)
 {
+  store_current_group_frames (sfdump());
   set_current_group (group_last_group ());
   return cmdret_new (RET_SUCCESS, NULL);
 }
@@ -5066,6 +5073,7 @@ cmd_gnew (int interactive UNUSED, struct cmdarg **args)
 {
   if (groups_find_group_by_name (ARG_STRING (0), 1))
     return cmdret_new (RET_FAILURE, "gnew: group already exists");
+  store_current_group_frames (sfdump());
   set_current_group (group_add_new_group (ARG_STRING(0)));
   return cmdret_new (RET_SUCCESS, NULL);
 }
@@ -5146,7 +5154,97 @@ cmd_gselect (int interactive, struct cmdarg **args)
   g = find_group (ARG_STRING(0));
 
   if (g)
-    set_current_group (g);
+    {
+      store_current_group_frames (sfdump());
+      set_current_group (g);
+    }
+  else
+    return cmd_groups (interactive, NULL);
+
+  return cmdret_new (RET_SUCCESS, NULL);
+}
+
+/* Restore group's sfdump, i.e. the screens' state when the group was last
+   used. */
+cmdret *
+cmd_grestore (int interactive, struct cmdarg **args)
+{
+  rp_group *g;
+  cmdret *ret = NULL;
+  struct sbuf *s;
+  char *tmp;
+  int i;
+
+  if (args[0])
+    g = find_group (ARG_STRING(0));
+  else
+    g = rp_current_group;
+
+  if (g)
+    {
+      if (g->sframes)
+        {
+          ret = sfrestore (g->sframes);
+        }
+      else
+        {
+          /* No saved sfdump: blanks screens by creating an sfdump with a single
+             empty frame per screen, then restoring it. */
+          s = sbuf_new (0);
+          for (i=0; i<num_screens; i++)
+            {
+              tmp = xsprintf ("(frame :number %d :x %d :y %d :width %d :height %d :screenw %d :screenh %d :window %ld :last-access %d :dedicated %d) %d%s",
+                              0, 0, 0,
+                              screen_width(&screens[i]), screen_height(&screens[i]),
+                              screens[i].width, screens[i].height,
+                              0, 0, 0,
+                              (rp_have_xinerama)?(screens[i].xine_screen_num):(screens[i].screen_num),
+                              /* don't add a comma to the last screen */
+                              (i < num_screens-1)?(","):(""));
+                        
+              sbuf_concat (s, tmp);
+              free (tmp);
+            }
+          tmp = sbuf_free_struct(s);
+          ret = sfrestore (tmp);
+          free (tmp);
+        }
+    }
+  else
+    return cmd_groups (interactive, NULL);
+
+  /* return success or failure according to what sfrestore returned */
+  return ret;
+}
+
+/* Clean screens by blanking frames containing windows not in the current
+   group. */
+cmdret *
+cmd_gclean (int interactive, struct cmdarg **args)
+{
+  rp_group *g;
+  rp_window *win;
+  rp_frame *cur;
+  int i;
+
+  if (args[0])
+    g = find_group (ARG_STRING(0));
+  else
+    g = rp_current_group;
+  
+  if (g)
+    {
+      for (i=0; i<num_screens; i++)
+        {
+          list_for_each_entry (cur, &(screens[i].frames), node)
+            {
+              win = find_window_number (cur->win_number);
+              /* blank frame if window doesn't belong to group */
+              if (win && !group_find_window(&g->mapped_windows, win))
+                blank_frame(cur);
+            }
+        }
+    }
   else
     return cmd_groups (interactive, NULL);
 
@@ -5471,12 +5569,10 @@ cmd_set (int interactive UNUSED, struct cmdarg **args)
       return result;
     }
 }
-
-cmdret *
-cmd_sfdump (int interactively UNUSED, struct cmdarg **args UNUSED)
+char *
+sfdump (void)
 {
   char screen_suffix[16];
-  cmdret *ret;
   struct sbuf *dump;
   rp_frame *cur;
   int i;
@@ -5501,13 +5597,21 @@ cmd_sfdump (int interactively UNUSED, struct cmdarg **args UNUSED)
           free (frameset);
         }
     }
-  ret = cmdret_new (RET_SUCCESS, "%s", sbuf_get (dump));
-  sbuf_free (dump);
+
+  return sbuf_free_struct (dump);
+}
+
+cmdret *
+cmd_sfdump (int interactively UNUSED, struct cmdarg **args UNUSED)
+{
+  char *s = sfdump();
+  cmdret *ret = cmdret_new (RET_SUCCESS, "%s", s);
+  free (s);
   return ret;
 }
 
 cmdret *
-cmd_sfrestore (int interactively UNUSED, struct cmdarg **args)
+sfrestore (char *sframes)
 {
   struct sbuf *buffer[num_screens];
   char *copy, *ptr, *token;
@@ -5519,7 +5623,7 @@ cmd_sfrestore (int interactively UNUSED, struct cmdarg **args)
   for (i = 0; i < num_screens; i++)
     buffer[i] = sbuf_new (0);
 
-  copy = xstrdup (ARG_STRING (0));
+  copy = xstrdup (sframes);
 
   token = strtok (copy, ",");
   if (token == NULL)
@@ -5578,6 +5682,12 @@ cmd_sfrestore (int interactively UNUSED, struct cmdarg **args)
 }
 
 cmdret *
+cmd_sfrestore (int interactively UNUSED, struct cmdarg **args)
+{
+  return sfrestore (ARG_STRING(0));
+}
+
+cmdret *
 cmd_sdump (int interactive UNUSED, struct cmdarg **args UNUSED)
 {
   cmdret *ret;
diff --git a/src/actions.h b/src/actions.h
index 519c1a7..4e9dffa 100644
--- a/src/actions.h
+++ b/src/actions.h
@@ -131,6 +131,7 @@ RP_CMD (exchangeright);
 RP_CMD (swap);
 RP_CMD (frestore);
 RP_CMD (fselect);
+RP_CMD (gclean);
 RP_CMD (gdelete);
 RP_CMD (getenv);
 RP_CMD (gmerge);
@@ -143,6 +144,7 @@ RP_CMD (gprev);
 RP_CMD (gother);
 RP_CMD (gravity);
 RP_CMD (grename);
+RP_CMD (grestore);
 RP_CMD (groups);
 RP_CMD (gselect);
 RP_CMD (h_split);
@@ -234,6 +236,8 @@ void free_keymaps (void);
 char *wingravity_to_string (int g);
 rp_action* find_keybinding (KeySym keysym, unsigned int state, rp_keymap *map);
 rp_action* find_keybinding_by_action (char *action, rp_keymap *map);
+char *sfdump (void);
+cmdret *sfrestore (char *sframes);
 
 
 #endif /* ! _RATPOISON_ACTIONS_H */
diff --git a/src/data.h b/src/data.h
index 2be9a98..172883f 100644
--- a/src/data.h
+++ b/src/data.h
@@ -147,6 +147,9 @@ struct rp_group
 
   /* This structure can exist in a list. */
   struct list_head node;
+
+  /* screens' frame dump from the last time this group was used */
+  char *sframes;
 };
 
 struct rp_screen
diff --git a/src/globals.c b/src/globals.c
index 05ebe28..6ad16bb 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -235,6 +235,7 @@ LIST_HEAD (rp_switch_screen_hook);
 LIST_HEAD (rp_quit_hook);
 LIST_HEAD (rp_restart_hook);
 LIST_HEAD (rp_delete_window_hook);
+LIST_HEAD (rp_move_to_group_hook);
 LIST_HEAD (rp_new_window_hook);
 LIST_HEAD (rp_title_changed_hook);
 
@@ -245,6 +246,7 @@ struct rp_hook_db_entry rp_hook_db[]=
    {"switchgroup",      &rp_switch_group_hook},
    {"switchscreen",      &rp_switch_screen_hook},
    {"deletewindow",     &rp_delete_window_hook},
+   {"movetogroup",      &rp_move_to_group_hook},
    {"quit",             &rp_quit_hook},
    {"restart",          &rp_restart_hook},
    {"newwindow",	&rp_new_window_hook},
diff --git a/src/globals.h b/src/globals.h
index 12ccffd..622fefd 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -183,6 +183,7 @@ extern struct list_head rp_switch_frame_hook;
 extern struct list_head rp_switch_group_hook;
 extern struct list_head rp_switch_screen_hook;
 extern struct list_head rp_delete_window_hook;
+extern struct list_head rp_move_to_group_hook;
 extern struct list_head rp_quit_hook;
 extern struct list_head rp_restart_hook;
 extern struct list_head rp_new_window_hook;
diff --git a/src/group.c b/src/group.c
index 2bbfead..654f131 100644
--- a/src/group.c
+++ b/src/group.c
@@ -137,6 +137,7 @@ group_new (int number, char *name)
   g->numset = numset_new();
   INIT_LIST_HEAD (&g->unmapped_windows);
   INIT_LIST_HEAD (&g->mapped_windows);
+  g->sframes = NULL;
 
   return g;
 }
@@ -148,6 +149,7 @@ group_free (rp_group *g)
     free (g->name);
   numset_free (g->numset);
   numset_release (group_numset, g->number);
+  free (g->sframes);
   free (g);
 }
 
@@ -595,6 +597,8 @@ group_move_window (rp_group *to, rp_window *win)
   /* and shove it into the other one. */
   we->number = numset_request (to->numset);
   group_insert_window (&to->mapped_windows, we);
+
+  hook_run (&rp_move_to_group_hook);
 }
 
 void
@@ -637,6 +641,16 @@ set_current_group (rp_group *g)
   hook_run (&rp_switch_group_hook);
 }
 
+void
+store_current_group_frames (char *sframes)
+{
+  if (rp_current_group)
+    {
+      free (rp_current_group->sframes);
+      rp_current_group->sframes = sframes;
+    }
+}
+
 int
 group_delete_group (rp_group *g)
 {
diff --git a/src/group.h b/src/group.h
index 1081043..310d31a 100644
--- a/src/group.h
+++ b/src/group.h
@@ -68,6 +68,7 @@ void group_move_window (rp_group *to, rp_window *win);
 void groups_merge (rp_group *from, rp_group *to);
 
 void set_current_group (rp_group *g);
+void store_current_group_frames (char *sframes);
 
 rp_window *group_last_window_by_class (rp_group *g, char *class);
 rp_window *group_last_window_by_class_complement (rp_group *g, char *class);
ratpoisonrc_workspaces (text/plain, 923 B)
# workspaces

# add groups
# (in a single exec to avoid race and making sure the group doesn't
#  already exist)
exec ratpoison -c groups | grep -q '^1' || ratpoison -c "gnewbg 1"; ratpoison -c groups | grep -q '^2' || ratpoison -c "gnewbg 2"; ratpoison -c groups | grep -q '^3' || ratpoison -c "gnewbg 3"

# hooks
# restore last seen frames for newly selected group
addhook switchgroup grestore
# hide windows which no longer belong to this group
addhook switchgroup gclean
# also hide when moving a window, so it will be hidden from the current group
addhook movetogroup gclean
# display groups list
addhook switchgroup groups

# bindings
# select group
definekey root 1 gselect 0
definekey root 2 gselect 1
definekey root 3 gselect 2
definekey root 4 gselect 3
# move window to group
definekey root exclam     gmove 0
definekey root at         gmove 1
definekey root numbersign gmove 2
definekey root dollar     gmove 3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.