Can I have a window showing small live preview of another workspace?
EDIT
(New answer)
DONE.
The answer below is now available in a polished form, as an indicator, as a ppa for Trusty, Xenial, Yakkety and Zesty :
sudo apt-add-repository ppa:vlijm/windowspy
sudo apt-get update
sudo apt-get install windowspy
Th indicator (including preview window) is now nicely low on juice. Options include a settings window, setting window border size/color, window size.
In the meantime, I found it useful to keep an eye on the AU window; see if there are any messages :)
OLD ANSWER
(first second rough concept)
Have a minimized representation of a window on another workspace
To my own (big) surprise, it can be effectively done, be it with trickery and deceit; have an updated representation of a window on another workspace. Not fit to watch a movie, definitely good enough to keep an eye on a window elsewhere (example: my tv-card window):
How it works in practice
With the window in front, press a shortcut key:
(the window will minimize)
Move to another workspace, press the shortcut key again, a small representation of the window will appear, updated every 4 seconds:
The window always shows on top of other windows. As it is, the window is 300px (width), but can be set to any size.
To end it, press (again) the shortcut key. The small window will close, you will move to the viewport of the original window, which will appear again, unminimized.
The scripts
The control script
#!/usr/bin/env python3 import subprocess import os import sys import time # paths imagepath = os.path.join(os.environ["HOME"], ".showcase") wfile = os.path.join(imagepath, "currentwindow") vpfile = os.path.join(imagepath, "last_vp") # setup path if not os.path.exists(imagepath): os.mkdir(imagepath) def get(command): try: return subprocess.check_output(command).decode("utf-8").strip() except subprocess.CalledProcessError: pass def get_vp(): open(vpfile, "wt").write(get(["wmctrl", "-d"]).split()[5]) def run(command): subprocess.Popen(command) def convert_tohex(widxd): return widxd[:2]+((10-len(widxd))*"0")+widxd[2:] def check_windowtype(wid): check = get(["xprop", "-id", wid]) return not any([s in check for s in [ "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_DESKTOP"]]) def edit_winprops(wid, convert=True): run(["xdotool", "windowminimize", wid]) if convert: widxd = convert_tohex(hex(int(wid))) else: widxd = wid run(["wmctrl", "-i", "-r", widxd, "-b", "add,sticky"]) get_vp() open(os.path.join(imagepath, "currentwindow"), "wt").write(widxd) def initiate_min(): # if not, minmize window, write the file wid = get(["xdotool", "getactivewindow"]) if check_windowtype(wid): edit_winprops(wid) else: pidinfo = [l.split() for l in wlist.splitlines()] match = [l for l in pidinfo if all([ get(["ps", "-p", l[2], "-o", "comm="]) == "VirtualBox", not "Manager" in l])] if match: edit_winprops(match[0][0], convert=False) # windowlist wlist = get(["wmctrl", "-lp"]) if "Window preview" in wlist: # kill the miniwindow pid = get(["pgrep", "-f", "showmin"]) run(["kill", pid]) window = open(wfile).read().strip() viewport = open(vpfile).read().strip() run(["wmctrl", "-o", viewport]) time.sleep(0.3) run(["wmctrl", "-i", "-r", window, "-b", "remove,sticky"]) run(["wmctrl", "-ia", window]) os.remove(wfile) else: # check if windowfile exists wfileexists = os.path.exists(wfile) if wfileexists: # if file exists, try to run miniwindow window = open(wfile).read().strip() if window in wlist: # if the window exists, run! run(["showmin", window]) else: # if not, minmize window, write the file initiate_min() else: # if not, minmize window, write the file initiate_min()
The window representatiom
#!/usr/bin/env python3 import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject from PIL import Image import os import subprocess import time from threading import Thread import sys wid = sys.argv[1] xsize = 300 imagepath = os.path.join(os.environ["HOME"], ".showcase") if not os.path.exists(imagepath): os.mkdir(imagepath) img_in = os.path.join(imagepath, "image.png") resized = os.path.join(imagepath, "resized.png") def get_img(): subprocess.Popen([ "import", "-window", wid, "-resize", str(xsize), resized ]) get_img() class Splash(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Window preview") maingrid = Gtk.Grid() self.add(maingrid) self.image = Gtk.Image() # set the path to the image below self.resized = resized self.image.set_from_file(self.resized) maingrid.attach(self.image, 0, 0, 1, 1) maingrid.set_border_width(3) self.update = Thread(target=self.update_preview) self.update.setDaemon(True) self.update.start() def update_preview(self): while True: get_img() time.sleep(3) GObject.idle_add( self.image.set_from_file, self.resized, priority=GObject.PRIORITY_DEFAULT ) def miniwindow(): window = Splash() window.set_decorated(False) window.set_resizable(False) window.set_keep_above(True) window.set_wmclass("ShowCase", "showcase") window.connect("destroy", Gtk.main_quit) GObject.threads_init() window.show_all() window.move(70, 50) Gtk.main() miniwindow()
How to use
Install
python3-pil
,xdotool
andwmctrl
sudo apt-get install xdotool wmctrl python3-pil
Create, if it doesn't exist yet, the directory
~/bin
.- Copy script 1, he control script, as (exactly)
showcase_control
(no extension) in~/bin
, and make it executable. - Copy script 2, the mini- window script, as (exactly)
showmin
(no extension) in~/bin
, and make it executable. Log out and back in, and add the following command to a shortcut of your choice:
showcase_control
Choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:
showcase_control
and it should work!
- Press the key once to grab the current window
- move to the other workspace where you'd like the mini-window
- Press again to show the miniwindow
- Press again to move back to the original workspace, (automatically) un-minimize the original window and close the mini -one.
Downsides?
The setup, as it is currently, adds some work for your processor. On my (very) old system however, it adds (on average) appr. 4-5% I reckon, which I did not notice in any way.
Update: It turns out
import
can resize the image in one step, together with fetching the window image. This means a substantial reduction in processor load. At the same time the refresh time is shorter (3 seconds now), still at lower "costs".
Explanation
- My starting point was the way OP mentioned he wanted to use the option to keep an eye on a window on another workspace, waiting for either something to finish.
- While literally having an exact (mini) copy of a window on another workspace seems impossible, we can make an image of an existing window with the
import
-command, once we have the window id. While this both works on minimized windows or windows without focus, there is however one issue: the window needs to be on the current workspace. - The trick is then to temporarily (while the mini- window is used) make the window "sticky" (be virtually available on all workspaces) with
wmctrl
, but minimized at the same time. - Since all is done automatically, the difference effectively is none, since also returning to the initial viewport, "un-" sticky the original window and un- minimizing it, is done automatically.
In short:
- Pressing the shortcut once: targeted window is made sticky, but minimized
- Pressing it again (presumably on another workspace): a small mini- version of the window appears in the upper left corner, updated once per four seconds.
- Pressing it again: mini- window is closed, the desktop moves to the initial workspace of the window, the window is restored un- sticky and un- minimized.
Specifically for VirtualBox
When the VBox window is in front, it turns out Ubuntu shortcut keys are disabled(!), so the control script needs to be launched in another way. Below a few brief ones.
Option 1
I edited the control script. Now only in the case of VirtualBox:
Click anywhere on the desktop, Then press your shortcut key. After that, simply use the shortcut key to show the window and exit.
Explanation: The control script was made to exit if the window was of type "desktop", since you wouldn't want to minimize the desktop. Now the script first looks for possibly existing VirtualBox windows, to target, if the currently active window is the desktop.
Option 2
Copy the icon below (right- click -> save as), save it as
minwinicon.png
Copy the lines below into an empty file, save it as
minwin.desktop
in~/.local/share/applications
:[Desktop Entry] Type=Application Name=Window Spy Exec=showcase_control Icon=/path/to/minwinicon.png StartupNotify=false
You'd need to log out and back in for the launcher to "find" the local
~/bin
path!
Drag the icon on to the launcher to use it.
The second solution has an important downside: after using it from the launcher, it will keep blinking for a few seconds, waiting for a window to appear. During that, clicking again won't have any effect. That can be solved, as described here, but including that in this answer would really make it too long. If you want to use option two, please look into the link.