summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README560
-rw-r--r--src/colors.def3
-rw-r--r--src/context.c222
-rw-r--r--src/context.h23
-rw-r--r--src/desktop.c137
-rw-r--r--src/desktop.h4
-rw-r--r--src/key.c235
-rw-r--r--src/screen.c11
-rw-r--r--src/vwm.c12
-rw-r--r--src/vwm.h19
-rw-r--r--src/window.c185
-rw-r--r--src/window.h2
-rw-r--r--src/xwindow.c20
13 files changed, 902 insertions, 531 deletions
diff --git a/README b/README
index aeac987..588a458 100644
--- a/README
+++ b/README
@@ -1,72 +1,164 @@
\/\/\
-Built-ins:
+Modifiers:
- Mod1-RClick Focus the clicked window, but suppress raising
+ Mod1-r- Modifies some desktop/context/window switching
+ operations to be reversed. By itself is effectively
+ a noop.
+
+ Unlike the other modifiers, I haven't bothered
+ explicitly documenting -r's applicability below.
+ It's basically: Tab, Space, and `/Grave.
+
+ Mod1-Shift- Modifies many operations into "Migrates". A migrate
+ is a focused desktop/context changing operation which
+ brings the currently focused window along. By itself
+ is effectively a noop.
+
+ Mod1-s- Modifies window "Migrate" operations into "Send", by
+ itself is effectively a noop. (Was "shelve" window in
+ previous versions)
+
+ Mod1-Shift-s- Modifies some operations into a "Migrate"-like
+ "Send". Where a plain "Send" tends to create a new
+ empty desktop for receiving the sent window, when
+ combined with Shift, an existing focused desktop at
+ the destination will be used to receive the sent
+ window, which is how migrates work, but unlike
+ migrate, no actual switching occurs.
+
+ These also haven't been explicitly documented below,
+ currently it's basically: `/Grave, and 0-9.
+
+
+Built-in operations:
+
+ Mod1-RClick Focus the clicked window, but suppress raising.
Mod1-RClick-drag Focus the clicked window, suppress raising, resizing
- the window from its nearest corner until drag
- completes
+ the window from its nearest corner until drag
+ completes.
- Mod1-LClick Focus and raise the clicked window
+ Mod1-LClick Focus and raise the clicked window.
Mod1-LClick-drag Focus and raise the clicked window, moving the window
- until the drag completes
+ until the drag completes.
-* Mod1-l Switch to virtual desktop to the right (if exists)
+* Mod1-l Switch to virtual desktop to the right (if exists).
-* Mod1-h Switch to virtual desktop to the left (if exists)
+* Mod1-h Switch to virtual desktop to the left (if exists).
Mod1-j Lower focused window, if the focused window is in
- "allscreen" mode it will simply be fullscreened first
- without lowering
+ "allscreen" mode it will simply be fullscreened first
+ without lowering.
Mod1-k [-k [-k] Raise focused window [a second k raises and
[-k] [-k]] fullscreens the window [a third k raises and
- "allscreens" without a visible border [a fourth k
- raises and fullscreens across all heads [a fifth k
- raises and "allscreens" across all heads]]]
+ "allscreens" without a visible border [a fourth k
+ raises and fullscreens across all heads [a fifth k
+ raises and "allscreens" across all heads]]].
- Mod1-Shift-k Shelve focused window and switch to the shelf context
+* Mod1-Shift-k Migrate the focused window to the next/upper context
+ (if exists), keeping the window focused.
- Mod1-Shift-j Migrate the focused window from the shelf to the last
- focused virtual desktop, switch to that virtual
- desktop, and focus the migrated window
+* Mod1-Shift-j Migrate the focused window to the previous/lower
+ context (if exists), keeping the window focused.
* Mod1-Shift-l Migrate the focused window to the virtual desktop
- to the right (if exists), keeping the window focused
+ to the right (if exists), keeping the window focused.
* Mod1-Shift-h Migrate the focused window to the virtual desktop
- to the left (if exists), keeping the window focused
+ to the left (if exists), keeping the window focused.
+
+* Mod1-s-k Send the focused window to the next/upper context's
+ focused desktop (if exists), keeping the window
+ focused in the destination, but without switching to
+ the destination.
+
+* Mod1-s-j Send the focused window to the previous/lower
+ context's focused desktop (if exists), keeping the
+ window focused in the destination, but without
+ switching to the destination.
+
+* Mod1-s-l Send the focused window to the virtual desktop
+ to the right within the same context (if exists),
+ keeping the window focused in the destination, but
+ without switching to the destination.
+
+* Mod1-s-h Migrate the focused window to the virtual desktop
+ to the left within the same context (if exists),
+ keeping the window focused in the destination, but
+ without switching to the destination.
- Mod1-v Create a new virtual desktop and switch to it
+ Mod1-v Create a new empty virtual desktop within the current
+ context and switch to it.
- Mod1-Shift-v Create a new virtual desktop, move the focused window
- to it, and switch to it
+ Mod1-Shift-v Create a new virtual desktop within the current
+ context and switch to it, bringing the currently
+ focused window along (if present).
-* Mod1-Space Switch to the most recently used virtual desktop
- (like Mod1-Tab but for virtual desktops)
+ Mod1-s-v Create a new virtual desktop within the current
+ context, send the focused window to it, but don't
+ switch to the new virtual desktop.
-* Mod1-Shift-Space Switch to the most recently used virtual desktop,
- bringing the focused window with
+ Mod1-c Create a new empty virtual desktop within the next
+ unused context, implicitly creating a new context,
+ and switch to it.
- Mod1-` Alternate between shelf (if populated) and virtual
- desktop contexts
+ Mod1-Shift-c Create a new empty virtual desktop within the next
+ unused context, implicitly creating a new context,
+ and switch to it, bringing the currently focused
+ window along (if present).
- Mod1-s Shelve the focused window without switching to the
- shelf, adopting the newly shelved window as the
- focused window within the shelf
+ Mod1-s-c Create a new empty virtual desktop within the next
+ unused context, implicitly creating a new context,
+ send the focused window to it, but don't switch to
+ the new virtual desktop/context.
+
+ Mod1-0...9 Switch to the numbered context's focused desktop,
+ implicitly creating it if currently unused.
+
+ Mod1-Shift-0..9 Switch to the numbered context's focused desktop,
+ implicitly creating it if currently unused, bringing
+ the focused window along (if present).
+
+ Mod1-s-0..9 Send the focused window (if present) to a newly
+ created desktop within the numbered context.
+
+* Mod1-Space Switch to the next most recently used virtual desktop
+ within the current context (like Mod1-Tab but for
+ virtual desktops).
+
+* Mod1-Shift-Space Switch to the next most recently used virtual desktop
+ within the current context, bringing the focused
+ window along.
+
+ Mod1-s-Space Send the focused window to the next most recently
+ used desktop within the current context, keeping it
+ focused there, but without actually switching
+ desktops.
+
+ Mod1-` Switch to the next most recently used context's
+ focused virtual desktop.
+
+ Mod1-Shift-` Switch to the most recently used context's focused
+ virtual desktop, bringing the focused window along.
+
+ Mod1-s-` Send the currently focused window to a newly created
+ desktop within the next most recently used context,
+ without actually switching to it.
* Mod1-Tab Focus and raise the next window in the current
- context (shelf or virtual desktop), the focused
- window is not 'committed' as the MRU window until
- Mod1 is released, so you may peruse intermediate
- windows without affecting the order until releasing
- Mod1. In multihead configurations the next window
- selection is confined to the current screen.
+ virtual desktop, the focused window is not
+ 'committed' as the MRU window until Mod1 is released,
+ so you may peruse intermediate windows without
+ affecting the MRU order until releasing Mod1. In
+ multihead configurations the next window selection is
+ further confined to within the current screen+desktop.
Mod1-Shift-Tab Identical to Mod1-Tab except switches to the MRU
- window on another screen in a multihead configuration
+ window on another screen in a multihead
+ configuration.
Mod1-d Request the client destroy the focused window, or
destroy the current virtual desktop when no windows
@@ -122,8 +214,8 @@ Built-ins:
If a simultaneous second Mod1 is pressed at any point during a *'d
command, the window (and its desktop) focused when the *'d command
began will immediately be refocused - but not raised. This is
- intentional to simplify the arranging of obscured focused windows. If
- you find yourself restored to a desktop full of windows where your
+ intentional to simplify the arranging of obscured focused windows.
+ If you find yourself restored to a desktop full of windows where your
focused window is totally obscured/invisible, simply press Mod1-k to
raise it if desired.
@@ -142,85 +234,127 @@ Default launchers (configure by editing launchers.def and rebuild):
General:
- Newly created windows are raised but not focused unless they are the first
- window on an otherwise empty virtual desktop, then they are focused as well.
- When new windows appear on a populated virtual desktop, they are inserted
- immediately after the currently focused window in the windows list, so a
- Mod1-Tab will immediately focus new windows. Windows are kept in a MRU (Most
- Recently Used) order, keeping it efficient to alternate between an evolving
- set of active windows.
+ Newly created windows are raised but not focused unless they are the
+ first window on an otherwise empty virtual desktop, then they are focused
+ as well.
- The shelf is a sort of omnipresent and limited virtual desktop always
- available via Mod1-`, it only shows a single window at a time, Mod1-Tab
- cycles through the shelved windows. I use it as a place to stow xterms
- running backround processes I would like to retain the ability to observe the
- output of or interact with occasionally. Programs like transmission-gtk,
- cmus, wpa_supplicant under xterm, sometimes even iceweasel sessions find
- themselves in my shelf on a regular basis.
+ When new windows appear on a non-empty virtual desktop, they are inserted
+ immediately after the currently focused window in the windows list, so a
+ Mod1-Tab will immediately focus new windows. Windows are kept in a MRU
+ (Most Recently Used) order, keeping it efficient to alternate between an
+ evolving set of active windows. Mod1-r-Tab, using r as a modifier, may
+ be used to reverse the switching direction, handy for undoing an
+ accidental overshoot.
+
+ Like windows, virtual desktops are also kept on an MRU-ordered list.
+ These are cycled through via Mod1-Space, created with Mod1-v, and
+ destroyed with Mod1-d when empty. As with windows, Mod1-r-Space may be
+ used to reverse the switching direction.
+
+ Virtual desktops are grouped by contexts. Contexts are also kept on
+ MRU-ordered lists, which are cycled through via Mod1-`, created with
+ Mod1-c, and switched to by number with Mod1-0 through 9, which implicitly
+ creates the switched-to context if needed.
+
+ Prior versions of vwm included a "shelf" feature, this has been removed
+ in favor of the more generalized contexts. In the past Mod1-s would
+ "shelve" a window, and Mod1-` would switch between the shelf and focused
+ virtual desktop. Now Mod1-s is a modifier for sending windows elsewhere,
+ with one of the destinations being other contexts.
+
+ The shelf was used as a sort of junk drawer for things like xterms
+ running background processes without losing easy access to their
+ interactivity/output, while not polluting the active virtual desktops.
+
+ When vwm starts, it creates two contexts, numbers 0 and 1. 1 is what's
+ focused on startup, with 0 intended to serve as the shelf equivalent.
+ Now users may send windows to the shelf/junk drawer equivalent by
+ pressing Mod1-s-0, or Mod1-Shift-s-0, the former creating a new virtual
+ desktop for the sent window in context 0, the latter targeting the
+ existing focused desktop in context 0. Omitting the -s- from the former
+ switches to the focused desktop in contetxt 0, from the latter migrates
+ the focused window to the focused desktop in context 0.
Multihead/Xinerama:
- In multihead configurations new windows are placed on the screen containing
- the pointer if that screen is empty. Should the pointer be on a non-empty
- screen, then new windows are placed on the screen containing the currently
- focused window.
+ In multihead configurations, new windows are placed on the screen
+ containing the pointer, if that screen is empty. Should the pointer be
+ on a non-empty screen, then new windows are placed on the screen
+ containing the currently focused window.
New windows will automatically be focused if the screen they were placed on
is empty, even if their virtual desktop is not, which is a divergence from
the single-headed behavior where only lone windows on virtual desktops are
automatically focused.
+ Things like Mod1-[, Mod1-], and mod1-k-k respect screen boundaries of the
+ window's majority containing screen, and mod1-k-k-k mod1-k-k-k-k can be
+ used to violate those boundaries for creating fullscreen/allscreen
+ windows spanning multiple displays.
+
+ Multihead support is currently very limited. There's currently no
+ builtin for things like migrating windows to different screens, which
+ would be useful, especially for the mod1-[, mod1-], mod1-k-k style
+ autoconfigured windows, since they could automatically reconfigure
+ themselves migrating to different screen dimensions. The best way to
+ move windows to different screens is to Mod1-LClick-drag until the window
+ is at least mostly within the destination screen. At that point all the
+ autoconfigure window builtins utilize the most-overlapped screen as the
+ container.
+
Composite/Monitoring:
- One reason vwm was created was to provide a simplified platform for the
- research and development of a window manager with integrated omnipresent
- first-class local process monitoring. Early attempts were made to modify an
- existing window manager (WindowMaker) which produced unsatisfactory though
- inspiring results. The window managers vwm[12] were created shortly after to
- flesh out the interaction model and solidify an perfectly usable and easily
- modified window manager foundation, while libvmon was created in parallel to
- facilitate the efficient process monitoring required for such a task.
-
- After a number of iterations it was found that the Composite extension (along
- with the accompanying Damage and Render extensions) would give the best
- results on a modern Xorg linux system. Compositing hurts the rendering
- performance of X applications significantly however, so a hybrid model has
- been employed.
-
- Monitoring overlays visibility is toggled using Mod1-Semicolon, the sample
- rate is increased using Mod1-Right, and decreased using Mod1-Left.
-
- When the monitoring is not visible, vwm3 continues to leave X in immediate
- rendering mode with no additional overhead in the rendering pipeline, just
- like vwm2. The only additional overhead is the cost of regularly sampling
- process statistics and maintaining the state of window overlays (which does
- involve some X rendering of graphs and text, but does not introduce overhead
- to the drawing of client windows).
+ One reason vwm was created was to provide a simplified platform for
+ research and development of a window manager having integrated local
+ process CPU utilization monitoring. Early attempts were made to modify
+ an existing window manager (WindowMaker) which produced unsatisfactory
+ though inspiring results. The window managers vwm[12] were created
+ shortly after to flesh out the interaction model and solidify a tolerably
+ usable and easily modified window manager foundation, while libvmon was
+ created in parallel to facilitate the lightweight, high-frequency process
+ monitoring required for such a task.
+
+ After a number of iterations it was found that the Composite extension
+ (along with the accompanying Damage and Render extensions) would give the
+ best results on a modern Xorg linux system. Compositing hurts the
+ rendering performance of X applications significantly however, so a
+ hybrid model has been employed.
+
+ Monitoring overlays visibility is toggled using Mod1-Semicolon, the
+ sample rate is increased using Mod1-Right, and decreased using Mod1-Left.
+
+ When the monitoring is not visible, vwm3 continues to leave X in
+ immediate rendering mode with no additional overhead in the rendering
+ pipeline, just like vwm2. The only additional overhead is the cost of
+ regularly sampling process statistics and maintaining the state of window
+ overlays (which does involve some X rendering of graphs and text, but
+ does not introduce overhead to the drawing of client windows).
When monitoring overlays are made visible vwm3 temporarily becomes a
- compositing manager, redirecting the rendering of all windows to offscreen
- memory and assuming the responsibility of drawing all damaged contents to the
- root window on their behalf. This is what gives vwm3 the opportunity to draw
- alpha-blended contextual monitoring data over all of the windows, but it does
- come with a cost.
-
- Most modern GNU/Linux desktop environments are full-time composited, meaning
- all X clients are redirected at all times. This makes their draws more
- costly and latent due to all the additional copies being performed.
- Depending on how things have been implemented, in the interests of supporting
- things like transparent windows it also generally results in overlapping
- window regions being drawn repeatedly for every overlapping window from the
- bottom-up rather than just the top one.
-
- In vwm3 transparent windows are not supported, and shape windows (xeyes) are
- made rectangular in composited mode. This is so overlapping regions are only
- drawn once for the top windows having damage per burst of screen updates.
-
- Immediate rendering mode is restored upon disabling the monitoring overlays,
- restoring the drawing performance to vwm[12] levels where vwm3 is completely
- out of the drawing loop.
+ compositing manager, redirecting the rendering of all windows to
+ offscreen memory and assuming the responsibility of drawing all damaged
+ contents to the root window on their behalf. This is what gives vwm3 the
+ opportunity to draw alpha-blended contextual monitoring data over all of
+ the windows, but it does come with a cost.
+
+ Most modern GNU/Linux desktop environments are full-time composited,
+ meaning all X clients are redirected at all times. This makes their
+ draws more costly and latent due to all the additional copies being
+ performed. Depending on how things have been implemented, in the
+ interests of supporting things like transparent windows it also generally
+ results in overlapping window regions being drawn repeatedly for every
+ overlapping window from the bottom-up rather than just the top one.
+
+ In vwm3 transparent windows are not supported, and shape windows (xeyes)
+ are made rectangular in composited mode. This is so overlapping regions
+ are only drawn once for the top windows having damage per burst of screen
+ updates.
+
+ Immediate rendering mode is restored upon disabling the monitoring
+ overlays, restoring the drawing performance to vwm[12] levels where vwm3
+ is completely out of the drawing loop.
Here are some relevant things worth noting:
@@ -231,157 +365,167 @@ Composite/Monitoring:
the explicitly monitored client precesses.
- tmux orphans its backend process immediately at startup, discarding its
- parent->child relationship, so you don't get any monitoring of the commands
- running in your local tmux session. Use GNU screen instead.
+ parent->child relationship, so you don't get any monitoring of the
+ commands running in your local tmux session. Use GNU screen instead.
- - GNU screen orphans its backend on detach, so on reattach you've lost the
- parent->child relationship and find yourself in the same situation tmux
- puts you in immediately. I've developed an adopt() system call patch for
- the linux kernel to enable adopting orphans in this situation, but it
- hasn't been accepted. With this patch and a one line change to GNU screen
- the parent->child relationship is restored on reattach.
+ - GNU screen orphans its backend on detach, so on reattach you've lost
+ the parent->child relationship and find yourself in the same situation
+ tmux puts you in immediately. I've developed an adopt() system call
+ patch for the linux kernel to enable adopting orphans in this
+ situation, but it hasn't been accepted. With this patch and a one line
+ change to GNU screen the parent->child relationship is restored on
+ reattach.
- You may find patches for adding the adopt() system call to Linux and its
- integration into GNU screen in the patches/ subdirectory.
+ You may find patches for adding the adopt() system call to Linux and
+ its integration into GNU screen in the patches/ subdirectory.
- The top row of the overlays shows:
Total CPU Idle % (cyan):
- The height of every cyan vertical line reflects the %age of ticks since
- the previous sample which were spent in the idle task.
+ The height of every cyan vertical line reflects the %age of ticks
+ since the previous sample which were spent in the idle task.
Total CPU IOWait % (red):
- The height of every red vertical line reflects the %age of ticks since
- the previous sample which were lost to IO wait. Many people don't
- understand this correctly. This reflects opportunities to execute
- something other than the idle task which were lost because _all_
- runnable tasks at the time were blocked in IO.
+ The height of every red vertical line reflects the %age of ticks
+ since the previous sample which were lost to IO wait. Many people
+ don't understand this correctly. This reflects opportunities to
+ execute something other than the idle task which were lost because
+ _all_ runnable tasks at the time were blocked in IO.
An absence of IOWait does not mean nothing is blocked on IO. It just
- means there weren't opportunities to execute something which were lost due
- to waiting on IO.
+ means there weren't opportunities to execute something which were
+ lost due to waiting on IO.
- For example, lets say you have a dual core machine, and you launch two
- "yes > /dev/null &" commands. These two yes commands are essentially busy
- loops writing "yes" to /dev/null, they will always be runnable, and you
- will see a top row in the overlay devoid of any cyan _or_red_ while they
- execute.
+ For example, lets say you have a dual core machine, and you launch
+ two "yes > /dev/null &" commands. These two yes commands are
+ essentially busy loops writing "yes" to /dev/null, they will always
+ be runnable, and you will see a top row in the overlay devoid of any
+ cyan _or_red_ while they execute.
While they execute run something like:
"sudo echo 2 > /proc/sys/vm/drop_caches && du /"
- Still no IOWait in the top row. We know that du is blocking on IO, the
- caches are empty, but because there is always something runnable on the
- two cores thanks to the two yes commands, we'll never see IOWait. The
- other runnable processes mask the IOWait from our visibility.
+ Still no IOWait in the top row. We know that du is blocking on IO,
+ the caches are empty, but because there is always something runnable
+ on the two cores thanks to the two yes commands, we'll never see
+ IOWait. The other runnable processes mask the IOWait from our
+ visibility.
Now kill the two yes commands and rerun the du command, watch the top
- row. Some red should appear, the red indicates that there was CPU time
- available for running something, and the _only_ things available for that
- time was blocked in IO. Had there something else runnable, we wouldn't
- see the time lost to IOWait.
+ row. Some red should appear, the red indicates that there was CPU
+ time available for running something, and the _only_ things available
+ for that time was blocked in IO. Had there something else runnable,
+ we wouldn't see the time lost to IOWait.
When you see IOWait time, it's just saying nothing executed for that
- time, not for lack of any runnable tasks, just that all runnable tasks
- were blocked. It's still of value, but easily obscured on a system with
- any cpu-bound tasks constantly running.
+ time, not for lack of any runnable tasks, just that all runnable
+ tasks were blocked. It's still of value, but easily obscured on a
+ system with any cpu-bound tasks constantly running.
- The per-task (processes or threads) rows of the overlays show:
User CPU % (cyan):
- The height of every cyan vertical line reflects the %age of ticks since
- the previous sample which were spent executing this task in the user
- context.
+ The height of every cyan vertical line reflects the %age of ticks
+ since the previous sample which were spent executing this task in the
+ user context.
System CPU % (red):
- The height of every red vertical line reflects the %age of ticks since
- the previous sample which were spent executing this task in kernel/system
- context.
-
- Task monitoring beginning and ending is indicated with solid and checkered
- vertical bars, respectively. These generally coincide with the task clone
- and exit, but not always, especially the cloning.
-
- - You can pause sampling by lowering its rate (Mod1-Left) to 0Hz. Just be
- aware that this also pauses the updating of the overlay contents, so window
- resizes won't be adapted to in the overlay until increasing the sample rate
- (Mod1-Right). Pausing is useful if you've seen something interesting and
- would like to screenshot, study, or discuss. BTW, to take a screenshot
- when composited you have to capture the root window. If you capture the
- client window, you won't get the overlays, you'll just get the redirected
- window contents. Compositing mode composites everything into the root
- window, when you're interacting with the composited vwm3, you're looking at
- the root window the entire time.
-
- - The sample rate will automatically be lowered by vwm3 when it detects that
- it's having trouble maintaining the current one. If you have many windows
- or have a slow or heavily loaded processor/GPU they can become bottlenecks,
- especially at higher sample rates. Rather than continuing to bog down your
- X server (Xorg is not good at fairly scheduling clients under GPU
- contention), vwm3 will simply back off the sample rate as if you had hit
- Mod1-Left, to try ameliorate rather than exacerbate the situation.
+ The height of every red vertical line reflects the %age of ticks
+ since the previous sample which were spent executing this task in
+ kernel/system context.
+
+ Task monitoring beginning and ending is indicated with solid and
+ checkered vertical bars, respectively. These generally coincide with
+ the task clone and exit, but not always, especially the cloning.
+
+ - You can pause sampling by lowering its rate (Mod1-Left) to 0Hz. Just
+ be aware that this also pauses the updating of the overlay contents, so
+ window resizes won't be adapted to in the overlay until increasing the
+ sample rate (Mod1-Right). Pausing is useful if you've seen something
+ interesting and would like to screenshot, study, or discuss. BTW, to
+ take a screenshot when composited you have to capture the root window.
+ If you capture the client window, you won't get the overlays, you'll
+ just get the redirected window contents. Compositing mode composites
+ everything into the root window, when you're interacting with the
+ composited vwm3, you're looking at the root window the entire time.
+
+ - The sample rate will automatically be lowered by vwm3 when it detects
+ that it's having trouble maintaining the current one. If you have many
+ windows or have a slow or heavily loaded processor/GPU they can become
+ bottlenecks, especially at higher sample rates. Rather than continuing
+ to bog down your X server (Xorg is not good at fairly scheduling
+ clients under GPU contention), vwm3 will simply back off the sample
+ rate as if you had hit Mod1-Left, to try ameliorate rather than
+ exacerbate the situation.
- The monitoring is implemented using sampling, not tracing. Below the
current process heirarchy for every window there is an exited tasks
- snowflakes section filling the remaining space. Do not mistake this for
- something lossless like bash history or strace output, it's lossy since
- it's produced from sampled data. In part to try avoid interpretation of
- these as a reliable process history I refer to them as snowflakes in the
- code, since they fall downwards and sideways.
+ snowflakes section filling the remaining space. Do not mistake this
+ for something lossless like bash history or strace output, it's lossy
+ since it's produced from sampled data. In part to try avoid
+ interpretation of these as a reliable process history I refer to them
+ as snowflakes in the code, since they fall downwards and sideways.
With sufficiently high sample rates the output starts to take on the
- appearance of tracing, and while it may happen to capture every process in
- slower executions, most automata will execute entire commands in the time
- between samples. So try keep this in mind before thinking something's
- broken because you don't see something you expected in the snowflakes.
+ appearance of tracing, and while it may happen to capture every process
+ in slower executions, most automata will execute entire commands in the
+ time between samples. So try keep this in mind before thinking
+ something's broken because you don't see something you expected in the
+ snowflakes.
Some artifacts you might notice due to this which are not bugs are:
- "#missed it!" being shown as the command name, this happens when
- libvmon caught the process but the process exited before libvmon caught
- a sample of the name.
+ libvmon caught the process but the process exited before libvmon
+ caught a sample of the name.
- A parent's command name in the child when a different command was
executed. In UNIX systems processes fork before execing the new
command, in that window of time between the fork and exec, the child
- process is a clone of the parent, command and argv included. Sometimes
- the sample catches at just the right moment to see this in action.
+ process is a clone of the parent, command and argv included.
+ Sometimes the sample catches at just the right moment to see this in
+ action.
- Varying outputs in seeming identical actions. Things like simply
- launching xterm may produce no snowflakes at all in the new xterm, or a
- few like "bash" "dircolors -b" and "utempter add :0", especially if you
- have the sample rate turned up and cause some load on the system to slow
- the xterm and interactive bash startup scripts down.
+ launching xterm may produce no snowflakes at all in the new xterm,
+ or a few like "bash" "dircolors -b" and "utempter add :0",
+ especially if you have the sample rate turned up and cause some load
+ on the system to slow the xterm and interactive bash startup scripts
+ down.
- - In the interests of being efficient, nothing is being logged historically.
- The snowflakes area is all you get, which is limited to the free pixel
- space below the instantaneous process heirarchy within the window.
+ - In the interests of being efficient, nothing is being logged
+ historically. The snowflakes area is all you get, which is limited to
+ the free pixel space below the instantaneous process heirarchy within
+ the window.
- Everything which falls off the edges of the screen is lost forever, with
- the exception of windows which have been made smaller than they were.
+ Everything which falls off the edges of the screen is lost forever,
+ with the exception of windows which have been made smaller than they
+ were.
- You cannot scroll down or to the right to see older snowflakes or graphs.
+ You cannot scroll down or to the right to see older snowflakes or
+ graphs.
You cannot search the snowflakes.
- The native text and numeric representations of the sampled data is not kept
- any longer than the current sample, just long enough to update the
+ The native text and numeric representations of the sampled data is not
+ kept any longer than the current sample, just long enough to update the
overlays. From that point on the information exists only as visualized
- pixels in the overlay layers with no additional indexing or relationships
- being maintained with the currently monitored state.
+ pixels in the overlay layers with no additional indexing or
+ relationships being maintained with the currently monitored state.
- You can wipe the snowflakes of the focused window with Mod1-Apostrophe
- - The client PID is found via the _NET_WM_PID X property. This must be set
- by the client, and not all clients cooperate (xpdf is one I've noticed).
+ - The client PID is found via the _NET_WM_PID X property. This must be
+ set by the client, and not all clients cooperate (xpdf is one I've
+ noticed).
- This is annoying especially considering the vast majority of X clients run
- on modern systems are local clients connected via UNIX domain sockets.
- These sockets support ancillary messages including SCM_CREDENTIALS, which
- contains the pid of the connected process. Some investigation into the
- Xorg sources found it already queries this information and has it on hand,
- but doesn't bother setting the _NET_WM_PID property even though it's well-
- positioned to do so.
+ This is annoying especially considering the vast majority of X clients
+ run on modern systems are local clients connected via UNIX domain
+ sockets. These sockets support ancillary messages including
+ SCM_CREDENTIALS, which contains the pid of the connected process. Some
+ investigation into the Xorg sources found it already queries this
+ information and has it on hand, but doesn't bother setting the
+ _NET_WM_PID property even though it's well- positioned to do so.
I've developed and submitted upstream a patch for Xorg which sets
_NET_WM_PID on local connections, it complements vwm3 nicely.
@@ -389,11 +533,11 @@ Composite/Monitoring:
You can find the patch in the patches directory.
- There are around 5 files kept open in /proc for every task monitored by
- vwm. This applies to children processes and threads alike, so on a busy
- system it's not unrealistic to exceed 1024, a common default open files
- ulimit for GNU/Linux distributions. You can generally change this limit
- for your user via configuration in /etc/security/limits.conf or
- /etc/security/limits.d/.
+ vwm. This applies to children processes and threads alike, so on a
+ busy system it's not unrealistic to exceed 1024, a common default open
+ files ulimit for GNU/Linux distributions. You can generally change
+ this limit for your user via configuration in /etc/security/limits.conf
+ or /etc/security/limits.d/.
TODO finish and polish this readme...
diff --git a/src/colors.def b/src/colors.def
index 97c0dc9..7c71da0 100644
--- a/src/colors.def
+++ b/src/colors.def
@@ -1,6 +1,3 @@
-color(focused_window_border, "Green")
-color(shelved_window_border, "purple")
color(unfocused_window_border, "DarkGray")
-color(shelved_console_border, "red")
color(rubberband, "Orange")
color(logo, "LimeGreen")
diff --git a/src/context.c b/src/context.c
index 32d7b3f..255480c 100644
--- a/src/context.c
+++ b/src/context.c
@@ -15,89 +15,163 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /* desktop/shelf context handling */
+ /* contexts (this is derived from desktops) */
+#include <assert.h>
#include <X11/Xlib.h>
+#include <stdlib.h>
+#include <string.h>
+#include "list.h"
#include "context.h"
#include "desktop.h"
#include "vwm.h"
#include "xwindow.h"
-#include "window.h"
-/* switch to the desired context if it isn't already the focused one, inform caller if anything happened */
-int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context)
+/* make the specified context the most recently used one */
+vwm_context_t * vwm_context_mru(vwm_t *vwm, vwm_context_t *context)
{
- vwm_context_t entry_context = vwm->focused_context;
-
- switch (vwm->focused_context) {
- vwm_xwindow_t *xwin;
- vwm_window_t *vwin;
-
- case VWM_CONTEXT_SHELF:
- if (desired_context == VWM_CONTEXT_SHELF)
- break;
-
- /* desired == DESKTOP && focused == SHELF */
-
- VWM_TRACE("unmapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name);
- vwm_win_unmap(vwm, vwm->focused_shelf);
- XFlush(VWM_XDISPLAY(vwm)); /* for a more responsive feel */
-
- /* map the focused desktop, from the top of the stack down */
- list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
- if (!(vwin = xwin->managed))
- continue;
-
- if (vwin->desktop == vwm->focused_desktop && !vwin->shelved) {
- VWM_TRACE("Mapping desktop window \"%s\"", xwin->name);
- vwm_win_map(vwm, vwin);
- }
- }
-
- if (vwm->focused_desktop->focused_window) {
- VWM_TRACE("Focusing \"%s\"", vwm->focused_desktop->focused_window->xwindow->name);
- XSetInputFocus(VWM_XDISPLAY(vwm), vwm->focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime);
- }
-
- vwm->focused_context = VWM_CONTEXT_DESKTOP;
- break;
-
- case VWM_CONTEXT_DESKTOP:
- /* unmap everything, map the shelf */
- if (desired_context == VWM_CONTEXT_DESKTOP)
- break;
-
- /* desired == SHELF && focused == DESKTOP */
-
- /* there should be a focused shelf if the shelf contains any windows, we NOOP the switch if the shelf is empty. */
- if (vwm->focused_shelf) {
- /* unmap everything on the current desktop */
- list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
- if (!(vwin = xwin->managed))
- continue;
-
- if (vwin->desktop == vwm->focused_desktop) {
- VWM_TRACE("Unmapping desktop window \"%s\"", xwin->name);
- vwm_win_unmap(vwm, vwin);
- }
- }
-
- XFlush(VWM_XDISPLAY(vwm)); /* for a more responsive feel */
-
- VWM_TRACE("Mapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name);
- vwm_win_map(vwm, vwm->focused_shelf);
- vwm_win_focus(vwm, vwm->focused_shelf);
-
- vwm->focused_context = VWM_CONTEXT_SHELF;
- }
- break;
-
- default:
- VWM_BUG("unexpected focused context %x", vwm->focused_context);
- break;
+ VWM_TRACE("MRU context: %p", context);
+ list_move(&context->contexts_mru, &vwm->contexts_mru);
+
+ return context;
+}
+
+
+/* return next MRU context relative to the supplied context */
+vwm_context_t * vwm_context_next_mru(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction)
+{
+ list_head_t *next;
+
+ switch (direction) {
+ case VWM_DIRECTION_FORWARD:
+ if (context->contexts_mru.next == &vwm->contexts_mru) {
+ next = context->contexts_mru.next->next;
+ } else {
+ next = context->contexts_mru.next;
+ }
+ break;
+
+ case VWM_DIRECTION_REVERSE:
+ if (context->contexts_mru.prev == &vwm->contexts_mru) {
+ next = context->contexts_mru.prev->prev;
+ } else {
+ next = context->contexts_mru.prev;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ return list_entry(next, vwm_context_t, contexts_mru);
+}
+
+
+/* return next context spatially relative to the supplied context, no wrap-around */
+vwm_context_t * vwm_context_next(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction)
+{
+ switch (direction) {
+ case VWM_DIRECTION_FORWARD:
+ if (context->contexts.next != &vwm->contexts)
+ context = list_entry(context->contexts.next, vwm_context_t, contexts);
+ break;
+
+ case VWM_DIRECTION_REVERSE:
+ if (context->contexts.prev != &vwm->contexts)
+ context = list_entry(context->contexts.prev, vwm_context_t, contexts);
+ break;
+ }
+
+ return context;
+}
+
+
+/* helper for automatically assigning context colors */
+static int next_context_color_idx(vwm_t *vwm)
+{
+ int counts[VWM_CONTEXT_COLOR_MAX] = {};
+ vwm_context_t *context;
+ int color = 0;
+
+ /* TODO: contexts should probably keep a window count,
+ * so this could skip empty contexts, then those
+ * would be automatically recycled.
+ */
+ list_for_each_entry(context, &vwm->contexts, contexts)
+ counts[context->color]++;
+
+ for (int i = 0; i < NELEMS(counts); i++) {
+ if (counts[i] < counts[color])
+ color = i;
+ }
+
+ return color;
+}
+
+
+/* create a context */
+/* if color = -1 one is automatically assigned,
+ * otherwise the supplied color is used.
+ */
+vwm_context_t * vwm_context_create(vwm_t *vwm, int color, vwm_desktop_t *desktop)
+{
+ vwm_context_t *context;
+
+ context = calloc(1, sizeof(vwm_context_t));
+ if (context == NULL) {
+ VWM_PERROR("Failed to allocate context");
+ goto _fail;
+ }
+
+ if (color < 0)
+ color = next_context_color_idx(vwm);
+
+ assert(color < NELEMS(vwm->context_colors));
+
+ context->color = color;
+
+ list_add_tail(&context->contexts, &vwm->contexts);
+ list_add_tail(&context->contexts_mru, &vwm->contexts_mru);
+
+ if (!desktop)
+ desktop = vwm_desktop_create(vwm, context);
+
+ context->focused_desktop = desktop;
+
+ return context;
+
+_fail:
+ return NULL;
+}
+
+
+/* destroy a context */
+void vwm_context_destroy(vwm_t *vwm, vwm_context_t *context)
+{
+ /* silently refuse to destroy a context having windows (for now) */
+ /* there's _always_ a focused window on a context having mapped windows */
+ if (context->focused_desktop && context->focused_desktop->focused_window)
+ return;
+
+ /* also silently refuse to destroy the last context (for now) */
+ if (context->contexts.next == context->contexts.prev)
+ return;
+
+ list_del(&context->contexts);
+ list_del(&context->contexts_mru);
+}
+
+
+/* find a context by color, creating one if needed */
+vwm_context_t * vwm_context_by_color(vwm_t *vwm, unsigned color)
+{
+ vwm_context_t *context;
+
+ list_for_each_entry(context, &vwm->contexts, contexts) {
+ if (context->color == color)
+ return context;
}
- /* return if the context has been changed, the caller may need to branch differently if nothing happened */
- return (vwm->focused_context != entry_context);
+ return vwm_context_create(vwm, color, NULL);
}
diff --git a/src/context.h b/src/context.h
index 1604bd3..5eebc43 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,14 +1,27 @@
#ifndef _CONTEXT_H
#define _CONTEXT_H
+#include "direction.h"
+#include "list.h"
+
typedef struct _vwm_t vwm_t;
+typedef struct _vwm_desktop_t vwm_desktop_t;
-typedef enum _vwm_context_t {
- VWM_CONTEXT_DESKTOP = 0, /* focus the desktop context */
- VWM_CONTEXT_SHELF, /* focus the shelf context */
- VWM_CONTEXT_OTHER /* focus the other context relative to the current one */
+/* contexts and desktops are *very* similar, they should likely share code,
+ * simply duplicating for now.
+ */
+typedef struct _vwm_context_t {
+ list_head_t contexts; /* global list of contexts in spatial created-in order */
+ list_head_t contexts_mru; /* global list of contexts in MRU order */
+ vwm_desktop_t *focused_desktop; /* the focused desktop on this context */
+ unsigned color; /* color used for focused border on this context */
} vwm_context_t;
-int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context);
+vwm_context_t * vwm_context_mru(vwm_t *vwm, vwm_context_t *context);
+vwm_context_t * vwm_context_create(vwm_t *vwm, int color, vwm_desktop_t *desktop);
+void vwm_context_destroy(vwm_t *vwm, vwm_context_t *context);
+vwm_context_t * vwm_context_next_mru(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction);
+vwm_context_t * vwm_context_next(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction);
+vwm_context_t * vwm_context_by_color(vwm_t *vwm, unsigned color);
#endif
diff --git a/src/desktop.c b/src/desktop.c
index 8abc4d6..d88fa93 100644
--- a/src/desktop.c
+++ b/src/desktop.c
@@ -33,26 +33,27 @@ vwm_desktop_t * vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop)
{
VWM_TRACE("MRU desktop: %p", desktop);
list_move(&desktop->desktops_mru, &vwm->desktops_mru);
+ vwm_context_mru(vwm, desktop->context);
return desktop;
}
/* focus a virtual desktop */
-/* this switches to the desktop context if necessary, maps and unmaps windows accordingly if necessary */
+/* this updates the focused context if necessary, maps and unmaps windows accordingly if necessary */
int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop)
{
XGrabServer(VWM_XDISPLAY(vwm));
XSync(VWM_XDISPLAY(vwm), False);
- /* if the context switched and the focused desktop is the desired desktop there's nothing else to do */
- if ((vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP) && vwm->focused_desktop != desktop) || vwm->focused_desktop != desktop) {
+ if (vwm->focused_desktop != desktop) {
vwm_xwindow_t *xwin;
vwm_window_t *vwin;
/* unmap the windows on the currently focused desktop, map those on the newly focused one */
list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
- if (!(vwin = xwin->managed) || vwin->shelved)
+ vwin = xwin->managed;
+ if (!vwin)
continue;
if (vwin->desktop == vwm->focused_desktop)
@@ -62,7 +63,8 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop)
XFlush(VWM_XDISPLAY(vwm));
list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
- if (!(vwin = xwin->managed) || vwin->shelved)
+ vwin = xwin->managed;
+ if (!vwin)
continue;
if (vwin->desktop == desktop)
@@ -70,6 +72,7 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop)
}
vwm->focused_desktop = desktop;
+ desktop->context->focused_desktop = desktop;
}
/* directly focus the desktop's focused window if there is one, we don't use vwm_win_focus() intentionally XXX */
@@ -84,53 +87,67 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop)
}
-/* return next MRU desktop relative to the supplied desktop, wraps-around */
+/* return next MRU desktop within the same context relative to the supplied desktop, wraps-around */
vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction)
{
vwm_desktop_t *next = desktop;
- /* this dance is necessary because the list head @ vwm->desktops_mru has no vwm_desktop_t container,
- * and we're exploiting the circular nature of the doubly linked lists, so we need to take care to skip
- * past the container-less head.
- */
- switch (direction) {
- case VWM_DIRECTION_FORWARD:
- if (next->desktops_mru.next == &vwm->desktops_mru) {
- next = list_entry(next->desktops_mru.next->next, vwm_desktop_t, desktops_mru);
- } else {
- next = list_entry(next->desktops_mru.next, vwm_desktop_t, desktops_mru);
- }
- break;
+ do {
+ /* this dance is necessary because the list head @ vwm->desktops_mru has no vwm_desktop_t container,
+ * and we're exploiting the circular nature of the doubly linked lists, so we need to take care to skip
+ * past the container-less head.
+ */
+ switch (direction) {
+ case VWM_DIRECTION_FORWARD:
+ if (next->desktops_mru.next == &vwm->desktops_mru) {
+ next = list_entry(next->desktops_mru.next->next, vwm_desktop_t, desktops_mru);
+ } else {
+ next = list_entry(next->desktops_mru.next, vwm_desktop_t, desktops_mru);
+ }
+ break;
- case VWM_DIRECTION_REVERSE:
- if (next->desktops_mru.prev == &vwm->desktops_mru) {
- next = list_entry(next->desktops_mru.prev->prev, vwm_desktop_t, desktops_mru);
- } else {
- next = list_entry(next->desktops_mru.prev, vwm_desktop_t, desktops_mru);
- }
- break;
+ case VWM_DIRECTION_REVERSE:
+ if (next->desktops_mru.prev == &vwm->desktops_mru) {
+ next = list_entry(next->desktops_mru.prev->prev, vwm_desktop_t, desktops_mru);
+ } else {
+ next = list_entry(next->desktops_mru.prev, vwm_desktop_t, desktops_mru);
+ }
+ break;
- default:
- assert(0);
- }
+ default:
+ assert(0);
+ }
+ } while (next->context != desktop->context);
return next;
}
-/* return next desktop spatially relative to the supplied desktop, no wrap-around */
+/* return next in-context desktop spatially relative to the supplied desktop, no wrap-around */
vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction)
{
switch (direction) {
- case VWM_DIRECTION_FORWARD:
- if (desktop->desktops.next != &vwm->desktops)
- return list_entry(desktop->desktops.next, vwm_desktop_t, desktops);
+ case VWM_DIRECTION_FORWARD: {
+ vwm_desktop_t *next = desktop;
+
+ while (next->desktops.next != &vwm->desktops) {
+ next = list_entry(next->desktops.next, vwm_desktop_t, desktops);
+ if (next->context == desktop->context)
+ return next;
+ }
break;
+ }
- case VWM_DIRECTION_REVERSE:
- if (desktop->desktops.prev != &vwm->desktops)
- return list_entry(desktop->desktops.prev, vwm_desktop_t, desktops);
+ case VWM_DIRECTION_REVERSE: {
+ vwm_desktop_t *next = desktop;
+
+ while (next->desktops.prev != &vwm->desktops) {
+ next = list_entry(next->desktops.prev, vwm_desktop_t, desktops);
+ if (next->context == desktop->context)
+ return next;
+ }
break;
+ }
default:
assert(0);
@@ -139,24 +156,39 @@ vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_directi
return desktop;
}
-/* create a virtual desktop */
-vwm_desktop_t * vwm_desktop_create(vwm_t *vwm)
+
+/* TODO: when "sending" windows to contexts, I currently always create a
+ * new desktop with this function. There should really be a "create if needed"
+ * variant which just returns an empty desktop in the target context, creating
+ * only if no empty one already exists, for "sending" purposes. The current
+ * approach tends to litter empty desktops unnecessarily.
+ */
+/* create a virtual desktop on the supplied context,
+ * if context is NULL a new one is created
+ * the desktop becomes the context's focused desktop if there isn't already one
+ */
+vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, vwm_context_t *context)
{
vwm_desktop_t *desktop;
desktop = calloc(1, sizeof(vwm_desktop_t));
if (desktop == NULL) {
VWM_PERROR("Failed to allocate desktop");
- goto _fail;
+ return NULL;
}
+ if (!context)
+ context = vwm_context_create(vwm, -1, desktop);
+
+ if (!context->focused_desktop)
+ context->focused_desktop = desktop;
+
+ desktop->context = context;
+
list_add_tail(&desktop->desktops, &vwm->desktops);
list_add_tail(&desktop->desktops_mru, &vwm->desktops_mru);
return desktop;
-
-_fail:
- return NULL;
}
@@ -169,16 +201,31 @@ void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop)
if (desktop->focused_window || (desktop->desktops.next == desktop->desktops.prev))
return;
- /* focus the MRU desktop that isn't this one if we're the focused desktop */
- if (desktop == vwm->focused_desktop) {
+ /* focus the desktop context's MRU desktop that isn't this one,
+ * if desktop is the context's focused desktop */
+ if (desktop == desktop->context->focused_desktop) {
vwm_desktop_t *next_desktop;
list_for_each_entry(next_desktop, &vwm->desktops_mru, desktops_mru) {
- if (next_desktop != desktop) {
- vwm_desktop_focus(vwm, next_desktop);
+ if (next_desktop != desktop && next_desktop->context == desktop->context) {
+ desktop->context->focused_desktop = next_desktop;
break;
}
}
+
+ /* if *still* the context's focused desktop, the context is finished.
+ * find a desktop from the next MRU context to focus if this desktop
+ * was the vwm focused one before destroying the context
+ */
+ if (desktop == desktop->context->focused_desktop) {
+ if (desktop == vwm->focused_desktop)
+ next_desktop = vwm_context_next_mru(vwm, desktop->context, VWM_DIRECTION_FORWARD)->focused_desktop;
+
+ vwm_context_destroy(vwm, desktop->context);
+ }
+
+ if (desktop == vwm->focused_desktop)
+ vwm_desktop_focus(vwm, next_desktop);
}
list_del(&desktop->desktops);
diff --git a/src/desktop.h b/src/desktop.h
index 1503407..597adb8 100644
--- a/src/desktop.h
+++ b/src/desktop.h
@@ -7,16 +7,18 @@
typedef struct _vwm_t vwm_t;
typedef struct _vwm_window_t vwm_window_t;
+typedef struct _vwm_context_t vwm_context_t;
typedef struct _vwm_desktop_t {
list_head_t desktops; /* global list of (virtual) desktops */
list_head_t desktops_mru; /* global list of (virtual) desktops in MRU order */
+ vwm_context_t *context; /* context this desktop belongs to */
vwm_window_t *focused_window; /* the focused window on this virtual desktop */
} vwm_desktop_t;
vwm_desktop_t * vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop);
int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop);
-vwm_desktop_t * vwm_desktop_create(vwm_t *vwm);
+vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, vwm_context_t *context);
void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop);
vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction);
vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction);
diff --git a/src/key.c b/src/key.c
index e1eee5d..da1a22b 100644
--- a/src/key.c
+++ b/src/key.c
@@ -31,6 +31,7 @@
static int key_is_grabbed; /* flag for tracking keyboard grab state */
static vwm_direction_t direction = VWM_DIRECTION_FORWARD; /* flag for reversing directional actions */
+static int send_it; /* flag for "sending" a migration operation without following it */
/* Poll the keyboard state to see if _any_ keys are pressed */
static int keys_pressed(vwm_t *vwm)
@@ -75,8 +76,7 @@ void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease)
vwm_win_mru(vwm, vwin);
/* make the focused desktop the most recently used */
- if (vwm->focused_context == VWM_CONTEXT_DESKTOP && vwm->focused_desktop)
- vwm_desktop_mru(vwm, vwm->focused_desktop);
+ vwm_desktop_mru(vwm, vwm->focused_desktop);
break;
@@ -85,6 +85,11 @@ void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease)
direction = VWM_DIRECTION_FORWARD;
break;
+ case XK_s:
+ VWM_TRACE("XK_s released with send_it=%i", send_it);
+ send_it = 0;
+ break;
+
default:
VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
break;
@@ -144,9 +149,32 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
direction = VWM_DIRECTION_REVERSE;
break;
- case XK_grave: /* toggle shelf visibility */
- vwm_context_focus(vwm, VWM_CONTEXT_OTHER);
+ case XK_s: /* "send" migrational actions */
+ VWM_TRACE("XK_s pressed with send_it=%i", send_it);
+ send_it = 1;
+ break;
+
+ case XK_grave: { /* cycle focused desktop by context */
+ vwm_context_t *next_context;
+
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+ next_context = vwm_context_next_mru(vwm, vwm->focused_desktop->context, direction);
+
+ if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the MRU context's MRU desktop */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_context->focused_desktop));
+ } else if (send_it) { /* "send" the focused window to a new desktop created on the MRU context */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, next_context)));
+ } else if (keypress->state & ShiftMask) {
+ /* migrate the focused window with the desktop focus to the MRU context's MRU desktop */
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, next_context->focused_desktop);
+ } else {
+ vwm_desktop_focus(vwm, next_context->focused_desktop);
+ }
break;
+ }
case XK_Tab: /* cycle focused window */
do_grab = 1; /* update MRU window on commit (Mod1 release) */
@@ -154,22 +182,53 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
/* focus the next window, note this doesn't affect MRU yet, that happens on Mod1 release */
if (vwin) {
if (keypress->state & ShiftMask) {
- vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_MASKED_VIOLATE);
+ /* TODO: in keeping with the Shift==migrate behavior, perhaps
+ * for Tab it should really do a in-desktop migration of sorts
+ * where the focused window swaps places with the next window?
+ */
+ VWM_TRACE("in-desktop migrate not implemented yet");
} else {
vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_RESPECT);
}
}
break;
+ case XK_backslash:
+ do_grab = 1;
+
+ if (vwin) {
+ if (keypress->state & ShiftMask) {
+ /* TODO: migrate window to another screen within this desktop,
+ * like VWM_FENCE_MASKED_VIOLATE would focus the next window on
+ * the next screen, but instead of focusing the next window on
+ * the next display, move the focused one to that next desktop.
+ *
+ * since screens are handled within vwm_win_focus_next() via
+ * the fence abstraction, but fences aren't exposed outside of
+ * their, it's non-trivial to implement here. I may want to
+ * break that out into a more public interface to make things
+ * more composable at the screen level.
+ */
+ VWM_TRACE("migrate window to screen not implemented yet");
+ } else {
+ vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_MASKED_VIOLATE);
+ }
+ }
+ break;
+
case XK_space: { /* cycle focused desktop utilizing MRU */
vwm_desktop_t *next_desktop;
- next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop, direction);
-
do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+ next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop, direction);
- if (keypress->state & ShiftMask) {
- /* migrate the focused window with the desktop focus to the most recently used desktop */
+ if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the MRU desktop */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_desktop));
+ } else if (send_it) { /* "send" the focused window to a new desktop in the current context, kind of an alias of send_it+XK_v */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwin->desktop->context)));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the most recently used desktop */
if (vwin)
vwm_win_migrate(vwm, vwin, next_desktop);
} else {
@@ -185,7 +244,7 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
} else { /* kindly destroy the focused window */
vwm_xwin_message(vwm, vwin->xwindow, vwm->wm_protocols_atom, vwm->wm_delete_atom);
}
- } else if (vwm->focused_context == VWM_CONTEXT_DESKTOP) {
+ } else {
/* destroy the focused desktop when destroy occurs without any windows */
vwm_desktop_destroy(vwm, vwm->focused_desktop);
}
@@ -201,80 +260,111 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
case XK_v: /* instantiate (and focus) a new (potentially empty, unless migrating) virtual desktop */
do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
- if (keypress->state & ShiftMask) {
- if (vwin) {
- /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */
- vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm));
- }
+ if (send_it) { /* "send" the focused window to a newly created virtual desktop, */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwin->desktop->context)));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, vwin->desktop->context));
} else {
- vwm_desktop_focus(vwm, vwm_desktop_create(vwm));
- vwm_desktop_mru(vwm, vwm->focused_desktop);
+ vwm_desktop_focus(vwm, vwm_desktop_create(vwm, vwm->focused_desktop->context));
}
break;
- case XK_h: /* previous virtual desktop, if we're in the shelf context this will simply switch to desktop context */
+ case XK_c: /* instantiate (and focus) a new (potentialy empty, unless migrating) virtual desktop in a new context */
do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
- if (keypress->state & ShiftMask) {
- if (vwin) {
- /* migrate the focused window with the desktop focus to the previous desktop */
- vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE));
- }
+ if (send_it) { /* "send" the focused window to a newly created virtual desktop in a new context */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, NULL)));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window to a newly created virtual desktop in a new context, focusing the new desktop simultaneously */
+
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, NULL));
} else {
- if (vwm->focused_context == VWM_CONTEXT_SHELF) {
- /* focus the focused desktop instead of the shelf */
- vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
- } else {
- /* focus the previous desktop */
- vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_REVERSE));
- }
+ vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL));
}
break;
- case XK_l: /* next virtual desktop, if we're in the shelf context this will simply switch to desktop context */
+ case XK_0:
+ case XK_1:
+ case XK_2:
+ case XK_3:
+ case XK_4:
+ case XK_5:
+ case XK_6:
+ case XK_7:
+ case XK_8:
+ case XK_9:
do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
- if (keypress->state & ShiftMask) {
- if (vwin) {
- /* migrate the focused window with the desktop focus to the next desktop */
- vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD));
- }
+ if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the specified context */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop));
+ } else if (send_it) { /* "send" the focused window to a new desktop created on the specified context */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwm_context_by_color(vwm, sym - XK_0))));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window to the specified context */
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop);
} else {
- if (vwm->focused_context == VWM_CONTEXT_SHELF) {
- /* focus the focused desktop instead of the shelf */
- vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
- } else {
- /* focus the next desktop */
- vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_FORWARD));
- }
+ vwm_desktop_focus(vwm, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop);
+ }
+ break;
+
+ case XK_h: /* previous virtual desktop spatially */
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (send_it) { /* "send" the focused window to the previous desktop */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE)));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the previous desktop */
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE));
+ } else { /* focus the previous desktop */
+ vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_REVERSE));
+ }
+ break;
+
+ case XK_l: /* next virtual desktop spatially */
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (send_it) { /* "send" the focused window to the next desktop */
+ if (vwin)
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD)));
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the next desktop */
+ if (vwin)
+ vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD));
+ } else { /* focus the next desktop */
+ vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_FORWARD));
}
break;
- case XK_k: /* raise or shelve the focused window */
+ case XK_k: /* raise or context-migrate the focused window up */
if (vwin) {
- if (keypress->state & ShiftMask) { /* shelf the window and focus the shelf */
- if (vwm->focused_context != VWM_CONTEXT_SHELF) {
- /* shelve the focused window while focusing the shelf */
- vwm_win_shelve(vwm, vwin);
- vwm_context_focus(vwm, VWM_CONTEXT_SHELF);
- }
- } else {
- do_grab = 1;
+ do_grab = 1;
+
+ /* TODO: maybe bare send_it should create a new desktop in the next context,
+ * with Shift+send_it being the migrate-like send */
+ if (send_it) { /* "send" the focused window to the next context */
+ vwm_context_t *next_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_FORWARD);
+
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_context->focused_desktop));
+ } else if (keypress->state & ShiftMask) { /* migrate the window and focus the new context */
+ vwm_context_t *next_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_FORWARD);
+ vwm_win_migrate(vwm, vwin, next_context->focused_desktop);
+ } else {
XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id);
- if (repeat_cnt == 1) {
- /* double: reraise & fullscreen */
+ if (repeat_cnt == 1) { /* double: reraise & fullscreen */
vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
- } else if (repeat_cnt == 2) {
- /* triple: reraise & fullscreen w/borders obscured by screen perimiter */
+ } else if (repeat_cnt == 2) { /* triple: reraise & fullscreen w/borders obscured by screen perimiter */
vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_ALL);
} else if (vwm->xinerama_screens_cnt > 1) {
- if (repeat_cnt == 3) {
- /* triple: reraise & fullscreen across all screens */
+ if (repeat_cnt == 3) { /* triple: reraise & fullscreen across all screens */
vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_FULL);
- } else if (repeat_cnt == 4) {
- /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */
+ } else if (repeat_cnt == 4) { /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */
vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_ALL);
}
}
@@ -283,13 +373,20 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
}
break;
- case XK_j: /* lower or unshelve the focused window */
+ case XK_j: /* lower or context-migrate the focused window down */
if (vwin) {
- if (keypress->state & ShiftMask) { /* unshelf the window to the focused desktop, and focus the desktop */
- if (vwm->focused_context == VWM_CONTEXT_SHELF) {
- /* unshelve the focused window, focus the desktop it went to */
- vwm_win_migrate(vwm, vwin, vwm->focused_desktop);
- }
+ do_grab = 1;
+
+ /* TODO: maybe bare send_it should create a new desktop in the previous context,
+ * with Shift+send_it being the migrate-like send */
+ if (send_it) { /* "send" the focused window to the previous context */
+ vwm_context_t *prev_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_REVERSE);
+
+ vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, prev_context->focused_desktop));
+ } else if (keypress->state & ShiftMask) { /* migrate the window and focus the new context */
+ vwm_context_t *prev_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_REVERSE);
+
+ vwm_win_migrate(vwm, vwin, prev_context->focused_desktop);
} else {
if (vwin->autoconfigured == VWM_WIN_AUTOCONF_ALL) {
vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
@@ -311,12 +408,6 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
}
break;
- case XK_s: /* shelve focused window */
- if (vwin && !vwin->shelved)
- vwm_win_shelve(vwm, vwin);
-
- break;
-
case XK_bracketleft: /* reconfigure the focused window to occupy the left or top half of the screen or left quarters on repeat */
if (vwin) {
do_grab = 1;
diff --git a/src/screen.c b/src/screen.c
index 32a0fc7..4449fb5 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -169,23 +169,20 @@ _out:
int vwm_screen_is_empty(vwm_t *vwm, const vwm_screen_t *scr, vwm_xwindow_t *ignore_xwin)
{
vwm_xwindow_t *xwin;
- int is_empty = 1;
list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
if (xwin == ignore_xwin || !xwin->client_mapped)
continue;
- if (!xwin->managed || (xwin->managed->desktop == vwm->focused_desktop && !xwin->managed->shelved)) {
+ if (!xwin->managed || xwin->managed->desktop == vwm->focused_desktop) {
/* XXX: it may make more sense to see what %age of the screen is overlapped by windows, and consider it empty if < some % */
/* This is just seeing if any window is predominantly within the specified screen, the rationale being if you had a focusable
* window on the screen you would have used the keyboard to make windows go there; this function is only used in determining
* wether a new window should go where the pointer is or not. */
- if (vwm_screen_overlaps_xwin(vwm, scr, xwin) >= 0.05) {
- is_empty = 0;
- break;
- }
+ if (vwm_screen_overlaps_xwin(vwm, scr, xwin) >= 0.05)
+ return 0;
}
}
- return is_empty;
+ return 1;
}
diff --git a/src/vwm.c b/src/vwm.c
index 0e47fb0..9bc60de 100644
--- a/src/vwm.c
+++ b/src/vwm.c
@@ -70,6 +70,8 @@ static vwm_t * vwm_startup(void)
goto _err;
}
+ INIT_LIST_HEAD(&vwm->contexts);
+ INIT_LIST_HEAD(&vwm->contexts_mru);
INIT_LIST_HEAD(&vwm->desktops);
INIT_LIST_HEAD(&vwm->desktops_mru);
INIT_LIST_HEAD(&vwm->windows_mru);
@@ -125,6 +127,11 @@ static vwm_t * vwm_startup(void)
#include "colors.def"
#undef color
+#define color(_num, _str) \
+ XAllocNamedColor(VWM_XDISPLAY(vwm), VWM_XCMAP(vwm), _str, &vwm->context_colors[_num], &vwm->context_colors[_num]);
+#include "context_colors.def"
+#undef color
+
XSelectInput(VWM_XDISPLAY(vwm), VWM_XROOT(vwm),
FocusChangeMask | PropertyChangeMask | SubstructureNotifyMask | SubstructureRedirectMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
XGrabKey(VWM_XDISPLAY(vwm), AnyKey, WM_GRAB_MODIFIER, VWM_XROOT(vwm), False, GrabModeAsync, GrabModeAsync);
@@ -133,8 +140,9 @@ static vwm_t * vwm_startup(void)
XSetInputFocus(VWM_XDISPLAY(vwm), VWM_XROOT(vwm), RevertToPointerRoot, CurrentTime);
- /* create initial virtual desktop */
- vwm_desktop_focus(vwm, vwm_desktop_create(vwm));
+ /* create initial contexts and desktop */
+ vwm_desktop_create(vwm, NULL); /* shelf */
+ vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL)); /* general */
vwm_desktop_mru(vwm, vwm->focused_desktop);
/* manage all preexisting windows */
diff --git a/src/vwm.h b/src/vwm.h
index a25e2b6..fec6a58 100644
--- a/src/vwm.h
+++ b/src/vwm.h
@@ -32,6 +32,15 @@
typedef struct _vwm_window_t vwm_window_t;
typedef struct _vwm_desktop_t vwm_desktop_t;
+/* this contortion is currently just to get VWM_CONTEXT_COLOR_MAX defined */
+typedef enum _vwm_context_color_t {
+#define color(_num, _str) \
+ VWM_CONTEXT_COLOR_ ## _num,
+#include "context_colors.def"
+#undef color
+ VWM_CONTEXT_COLOR_MAX
+} vwm_context_color_t;
+
typedef struct _vwm_t {
vwm_xserver_t *xserver; /* global xserver instance */
vwm_charts_t *charts; /* golbal charts instance */
@@ -45,24 +54,26 @@ typedef struct _vwm_t {
int xinerama_screens_cnt;
int done; /* global flag to cause vwm to quit */
+ list_head_t contexts; /* global list of all contexts in spatial created-in order */
+ list_head_t contexts_mru; /* global list of all contexts kept in MRU order */
list_head_t desktops; /* global list of all (virtual) desktops in spatial created-in order */
list_head_t desktops_mru; /* global list of all (virtual) desktops in MRU order */
list_head_t windows_mru; /* global list of all managed windows kept in MRU order */
list_head_t xwindows; /* global list of all xwindows kept in the X server stacking order */
+
vwm_window_t *console; /* the console window */
vwm_window_t *focused_origin; /* the originating window in a grabbed operation/transaction */
- vwm_desktop_t *focused_desktop; /* currently focused (virtual) desktop */
- vwm_window_t *focused_shelf; /* currently focused shelved window */
- vwm_context_t focused_context; /* currently focused context */
+ vwm_desktop_t *focused_desktop; /* currently focused desktop */
int priority; /* scheduling priority of the vwm process, launcher nices relative to this */
unsigned long fence_mask; /* global mask state for vwm_win_focus_next(... VWM_FENCE_MASKED_VIOLATE),
- * if you use vwm on enough screens to overflow this, pics or it didn't happen. */
+ * if you use vwm on enough screens to overflow this, pics or it didn't happen. */
struct colors {
#define color(_sym, _str) \
XColor _sym ## _color;
#include "colors.def"
#undef color
} colors;
+ XColor context_colors[VWM_CONTEXT_COLOR_MAX];
} vwm_t;
#endif
diff --git a/src/window.c b/src/window.c
index d1441f3..5410ba6 100644
--- a/src/window.c
+++ b/src/window.c
@@ -80,20 +80,10 @@ vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win)
}
-/* return the currently focused window (considers current context...), may return NULL */
+/* return the currently focused window, may return NULL */
vwm_window_t * vwm_win_get_focused(vwm_t *vwm)
{
- switch (vwm->focused_context) {
- case VWM_CONTEXT_SHELF:
- return vwm->focused_shelf;
-
- case VWM_CONTEXT_DESKTOP:
- return vwm->focused_desktop->focused_window;
-
- default:
- VWM_BUG("Unsupported context");
- assert(0);
- }
+ return vwm->focused_desktop->focused_window;
}
@@ -103,22 +93,15 @@ vwm_window_t * vwm_win_get_focused(vwm_t *vwm)
void vwm_win_set_focused(vwm_t *vwm, vwm_window_t *vwin)
{
/* update the border color accordingly */
- if (vwin->shelved) {
- /* set the border of the newly focused window to the shelved color */
- XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwin == vwm->console ? vwm->colors.shelved_console_border_color.pixel : vwm->colors.shelved_window_border_color.pixel);
- /* fullscreen windows in the shelf when focused, since we don't intend to overlap there */
- vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_POINTER, VWM_WIN_AUTOCONF_FULL); /* XXX TODO: for now the shelf follows the pointer, it's simple. */
- } else {
- if (vwin->desktop->focused_window)
- /* set the border of the previously focused window on the same desktop to the unfocused color */
- XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->desktop->focused_window->xwindow->id, vwm->colors.unfocused_window_border_color.pixel);
+ if (vwin->desktop->focused_window)
+ /* set the border of the previously focused window on the same desktop to the unfocused color */
+ XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->desktop->focused_window->xwindow->id, vwm->colors.unfocused_window_border_color.pixel);
- /* set the border of the newly focused window to the focused color */
- XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwm->colors.focused_window_border_color.pixel);
+ /* set the border of the newly focused window to the focused color */
+ XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwm->context_colors[vwin->desktop->context->color].pixel);
- /* persist this on a per-desktop basis so it can be restored on desktop switches */
- vwin->desktop->focused_window = vwin;
- }
+ /* persist this on a per-desktop basis so it can be restored on desktop switches */
+ vwin->desktop->focused_window = vwin;
}
@@ -131,7 +114,7 @@ void vwm_win_autoconf_magic(vwm_t *vwm, vwm_window_t *vwin, const vwm_screen_t *
if (!scr)
scr = vwm_screen_find(vwm, VWM_SCREEN_REL_RECT, x, y, width, height);
- if (!vwin->shelved && scr &&
+ if (scr &&
width == scr->width &&
height == scr->height) {
VWM_TRACE_WIN(vwin->xwindow->id, "auto-allscreened window");
@@ -329,21 +312,9 @@ _retry:
VWM_TRACE("VWM_FENCE_MASKED_VIOLATE fence_mask now: 0x%lx\n", vwm->fence_mask);
}
- if (vwin->shelved) {
- if (next != vwm->focused_shelf) {
- if (vwm->focused_context == VWM_CONTEXT_SHELF) {
- vwm_win_unmap(vwm, vwm->focused_shelf);
- XFlush(VWM_XDISPLAY(vwm));
- vwm_win_map(vwm, next);
- }
- vwm->focused_shelf = next;
- vwm_win_focus(vwm, next);
- }
- } else {
- if (next != next->desktop->focused_window) {
- vwm_win_focus(vwm, next);
- XRaiseWindow(VWM_XDISPLAY(vwm), next->xwindow->id);
- }
+ if (next != next->desktop->focused_window) {
+ vwm_win_focus(vwm, next);
+ XRaiseWindow(VWM_XDISPLAY(vwm), next->xwindow->id);
}
VWM_TRACE("vwin=%p xwin=%p name=\"%s\"", next, next->xwindow, next->xwindow->name);
@@ -352,48 +323,62 @@ _retry:
}
-/* shelves a window, if the window is focused we focus the next one (if possible) */
+/* "shelves" a window, if the window is focused we focus the next one (if exists) */
+/* originally there was a special shelf context having different semantics of a fullscreen
+ * window at a time, this evolved into generic contexts containing virtual
+ * desktops, and now shelving is just the process of sending a window to the bottom/first
+ * context into a newly created desktop there, in an unattended fashion (like an unattended migrate,
+ * to an assumed bottom destination context created for this purpose at startup, and into its own
+ * desktop there).
+ */
void vwm_win_shelve(vwm_t *vwm, vwm_window_t *vwin)
{
- /* already shelved, NOOP */
- if (vwin->shelved)
+ vwm_context_t *shelf = list_entry(vwm->contexts.next, vwm_context_t, contexts);
+ vwm_desktop_t *desktop;
+
+ /* already in the first, AKA "shelf" context, NOOP */
+ if (&vwin->desktop->context->contexts == vwm->contexts.next)
return;
/* shelving focused window, focus the next window */
if (vwin == vwin->desktop->focused_window)
vwm_win_mru(vwm, vwm_win_focus_next(vwm, vwin, VWM_DIRECTION_FORWARD, VWM_FENCE_RESPECT));
+ /* vwin appears to be alone */
if (vwin == vwin->desktop->focused_window)
- /* TODO: we can probably put this into vwm_win_focus_next() and have it always handled there... */
vwin->desktop->focused_window = NULL;
- vwin->shelved = 1;
- vwm_win_mru(vwm, vwin);
+ /* TODO: ^^^ there should probably be a helper for withdrawing a window
+ * from a desktop which handles the above focus next -> lone window
+ * nonsense, and hands back an orphan window to do whatever with.
+ */
+
+ /* shelved windows always get an empty desktop in the shelf context,
+ * look for an empty one and only create a new one if there is none
+ * to use.
+ */
+ vwin->desktop = NULL;
+ list_for_each_entry(desktop, &vwm->desktops_mru, desktops_mru) {
+ if (desktop->context == shelf && !desktop->focused_window) {
+ vwin->desktop = desktop;
+ break;
+ }
+ }
- /* newly shelved windows always become the focused shelf */
- vwm->focused_shelf = vwin;
+ if (!vwin->desktop)
+ vwin->desktop = vwm_desktop_create(vwm, shelf);
+ /* always leave the newly shelved window's desktop focused */
+ vwin->desktop->context->focused_desktop = vwin->desktop;
+ vwm_win_set_focused(vwm, vwin);
+ vwm_win_mru(vwm, vwin);
vwm_win_unmap(vwm, vwin);
}
-/* helper for (idempotently) unfocusing a window, deals with context switching etc... */
+/* helper for (idempotently) unfocusing a window */
void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin)
{
- /* if we're the focused shelved window, cycle the focus to the next shelved window if possible, if there's no more shelf, switch to the desktop */
- /* TODO: there's probably some icky behaviors for focused windows unmapping/destroying in unfocused contexts, we probably jump contexts suddenly. */
- if (vwin == vwm->focused_shelf) {
- VWM_TRACE("unfocusing focused shelf");
- vwm_win_focus_next(vwm, vwin, VWM_DIRECTION_FORWARD, VWM_FENCE_IGNORE);
-
- if (vwin == vwm->focused_shelf) {
- VWM_TRACE("shelf empty, leaving");
- /* no other shelved windows, exit the shelf context */
- vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
- vwm->focused_shelf = NULL;
- }
- }
-
/* if we're the focused window cycle the focus to the next window on the desktop if possible */
if (vwin->desktop->focused_window == vwin) {
VWM_TRACE("unfocusing focused window");
@@ -401,7 +386,7 @@ void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin)
}
if (vwin->desktop->focused_window == vwin) {
- VWM_TRACE("desktop empty");
+ VWM_TRACE("unfocused last window on desktop");
vwin->desktop->focused_window = NULL;
}
}
@@ -468,26 +453,23 @@ static void vwm_win_assimilate(vwm_t *vwm, vwm_window_t *vwin)
/* TODO: this is a good place to hook in a window placement algo */
/* on client-requested mapping we place the window */
- if (!vwin->shelved) {
- /* we place the window on the screen containing the the pointer only if that screen is empty,
- * otherwise we place windows on the screen containing the currently focused window */
- scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER);
- if (vwm_screen_is_empty(vwm, scr, vwin->xwindow)) {
- /* focus the new window if it isn't already focused when it's going to an empty screen */
- VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number);
- vwm_win_focus(vwm, vwin);
- } else {
- scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow);
- }
-
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- } else if (vwm->focused_context == VWM_CONTEXT_SHELF) {
- scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_shelf->xwindow);
- changes.x = scr->x_org;
- changes.y = scr->y_org;
+ /* we place the window on the screen containing the the pointer only if that screen is empty,
+ * otherwise we place windows on the screen containing the currently focused window */
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER);
+ if (vwm_screen_is_empty(vwm, scr, vwin->xwindow)) {
+ /* focus the new window if it isn't already focused when it's going to an empty screen */
+ VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number);
+ vwm_win_focus(vwm, vwin);
+ } else {
+ /* FIXME TODO: there's some situation where we get here but focused_desktop->focused_window == NULL,
+ * which shouldn't be possible; for there to be a non-empty screen, the focused_desktop must have a focused_window.
+ */
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow);
}
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+
/* XXX TODO: does this belong here? */
XGetWMNormalHints(VWM_XDISPLAY(vwm), xwin->id, vwin->hints, &vwin->hints_supplied);
XGetWindowAttributes(VWM_XDISPLAY(vwm), xwin->id, &attrs);
@@ -541,7 +523,6 @@ vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin)
vwin->desktop = vwm->focused_desktop;
vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE;
- vwin->shelved = (vwm->focused_context == VWM_CONTEXT_SHELF); /* if we're in the shelf when the window is created, the window is shelved */
vwin->client = xwin->attrs; /* remember whatever the current attributes are */
VWM_TRACE("hints: flags=%lx x=%i y=%i w=%i h=%i minw=%i minh=%i maxw=%i maxh=%i winc=%i hinc=%i basew=%i baseh=%i grav=%x",
@@ -572,9 +553,9 @@ vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin)
/* always raise newly managed windows so we know about them. */
XRaiseWindow(VWM_XDISPLAY(vwm), xwin->id);
- /* if the desktop has no focused window yet, automatically focus the newly managed one, provided we're on the desktop context */
- if (!vwm->focused_desktop->focused_window && vwm->focused_context == VWM_CONTEXT_DESKTOP) {
- VWM_TRACE("Mapped new window \"%s\" is alone on desktop, focusing", xwin->name);
+ /* if the desktop has no focused window yet, automatically focus the newly managed one */
+ if (!vwm->focused_desktop->focused_window) {
+ VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%s\", focusing", xwin->name, vwm->focused_desktop->name);
vwm_win_focus(vwm, vwin);
}
@@ -595,12 +576,34 @@ _fail:
void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop)
{
vwm_win_unfocus(vwm, vwin); /* go through the motions of unfocusing the window if it is focused */
- vwin->shelved = 0; /* ensure not shelved */
- vwin->desktop = desktop; /* assign the new desktop */
+ vwin->desktop = desktop; /* assign the new desktop */
vwm_desktop_focus(vwm, desktop); /* currently we always focus the new desktop in a migrate */
vwm_win_focus(vwm, vwin); /* focus the window so borders get updated */
vwm_win_mru(vwm, vwin); /* TODO: is this right? shouldn't the Mod1 release be what's responsible for this? I migrate so infrequently it probably doesn't matter */
- XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is raised */
+ XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is @ top of stack */
+}
+
+
+/* "send" a window to another desktop, no desktop/context switching occurs. */
+void vwm_win_send(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop)
+{
+ if (desktop == vwin->desktop)
+ return;
+
+ vwm_win_unfocus(vwm, vwin);
+ vwm_win_unmap(vwm, vwin);
+ vwin->desktop = desktop;
+
+ /* XXX: only focus the destination desktop when not the focused context, as
+ * it creates an awkward disconnect for the focused context's focused desktop
+ * to become updated to something else while looking at it without actually
+ * realizing that focus change like a migrate does.
+ */
+ if (vwm->focused_desktop->context != desktop->context)
+ desktop->context->focused_desktop = desktop;
+
+ vwm_win_set_focused(vwm, vwin);
+ XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is @ top of stack */
}
diff --git a/src/window.h b/src/window.h
index eef639e..daddf14 100644
--- a/src/window.h
+++ b/src/window.h
@@ -35,7 +35,6 @@ typedef struct _vwm_window_t {
unsigned int autoconfigured:3; /* autoconfigured window states (none/quarter/half/full/all) */
unsigned int mapping:1; /* is the window being mapped? (by vwm) */
unsigned int unmapping:1; /* is the window being unmapped? (by vwm) */
- unsigned int shelved:1; /* is the window shelved? */
} vwm_window_t;
@@ -78,6 +77,7 @@ void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin);
vwm_xwindow_t * vwm_win_unmanage(vwm_t *vwm, vwm_window_t *vwin);
vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin);
void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop);
+void vwm_win_send(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop);
#endif
diff --git a/src/xwindow.c b/src/xwindow.c
index 86e8e9d..1deaf33 100644
--- a/src/xwindow.c
+++ b/src/xwindow.c
@@ -62,31 +62,15 @@ vwm_xwindow_t * vwm_xwin_lookup(vwm_t *vwm, Window win)
}
-/* determine if a window is mapped (vwm-mapped) according to the current context */
+/* determine if a window is mapped (vwm-mapped) according to the focused context */
int vwm_xwin_is_mapped(vwm_t *vwm, vwm_xwindow_t *xwin)
{
vwm_window_t *vwin = xwin->managed;
- int ret = 0;
if (!xwin->client_mapped || !vwin)
return xwin->client_mapped;
- switch (vwm->focused_context) {
- case VWM_CONTEXT_SHELF:
- if (vwm->focused_shelf == vwin)
- ret = 1;
- break;
-
- case VWM_CONTEXT_DESKTOP:
- if (vwm->focused_desktop == vwin->desktop && !vwin->shelved)
- ret = 1;
- break;
-
- default:
- VWM_BUG("Unsupported context");
- }
-
- return ret;
+ return (vwm->focused_desktop == vwin->desktop);
}
© All Rights Reserved