Linux Desktop Setup 01/10/25 ---------------------------------------------------------------------- For whatever reason, I can't simply be satisfied with my computing environment. I mean, at some level I am, but the ability to change almost any aspect of it is far too appealing for me to ignore. And so I do change things up, often. Recently, I downloaded and compiled emwmw[1]. The Motif Window Manager was one of the first ones I ever used. Along with the ubiquitous twm, of cours. So when I saw that emwmw was in active development, I had to try it out. I don't have anything specific to report, except that I'm able to replicate some preferred functionality using the following additional applications (this may not be the best, and it is certainly not the only, way to do all this): * sxhkd for hotkey shortcuts * vdeskgui.py interface to vdesk for virtual desktops (code below) * trayer for a system tray * nm-applet, blueman-applet, and parcellite to fill that tray * tdc for a clock * alttab for custom task switching control I appreciate vdesk and the ability to "pin" things without any drama or overhead; I have two monitors and one hosts my amateur radio software primarily, which I want always visible. Of course, every bit of the limited functionality listed above is readily available in half a dozen other desktop environment solutions, without the hassle of setting it up myself. But I have those, and know how to use them. It was fun to setup a *mwm setup and make it work. [1] https://fastestcode.org/emwm.html Here's the code for vdeskgui.py. If you would please make a cleaner and better version and send it my way, it would be much appreciated ;) #!/usr/bin/env python3 # A simple gui for Adam Sampson's vdesk program, tfurrows@sdf.org # v0.2, 11/22/2024. This software is in the public domain from tkinter import * from tkinter import ttk import subprocess progname = "vdesk switcher" progsize = "312x35" totaldesks = 7 class Window(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.master = master # Build the desktop switching buttons self.pack(fill=BOTH, expand=1) for dnum in range(1, totaldesks+1): vdbtn = Button(self, text=str(dnum), command = lambda vdid=dnum: self.govd(vdid)) vdbtn.grid(row=0,column=dnum, padx=(2,0), pady=(2,0)) vdbtn = Button(self, text="*", command = self.gomenu) vdbtn.grid(row=0,column=totaldesks+2, padx=(2,0), pady=(2,0)) def govd(self,vdid): subprocess.run(["/usr/bin/vdesk", str(vdid)]) myid=self.findwin(progname) self.bringtoactive(myid) self.pintozero(myid) def gomenu(self): self.top = Toplevel(self) self.top.title("Open Windows") self.top.geometry('578x200') vbar = Scrollbar(self.top, orient=VERTICAL) vbar.pack(fill=Y, side=RIGHT, expand=FALSE) self.canvas = Canvas(self.top, bd=0, highlightthickness=0, scrollregion=(0,0,578,500)) self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) ctlframe = Frame(self.canvas) ctlframe.pack(fill=BOTH, expand=1) vbar.config(command = self.canvas.yview) self.canvas.create_window(0, 0, window=ctlframe, anchor="nw") self.canvas.config(yscrollcommand=vbar.set) allwins = self.getwins() r=0 for window_id, win_name in allwins: winlbl = Label(ctlframe, text=win_name[:64]) winlbl.grid(row=r,column=0, sticky=NW, pady=(10,10)) bringbtn = Button(ctlframe, text = 'Bring', command = lambda winid=window_id: self.bringtoactive(winid)) bringbtn.grid(row=r,column=1) pinbtn = Button(ctlframe, text = 'Pin', command = lambda winid=window_id: self.pintozero(winid)) pinbtn.grid(row=r,column=2) r=r+1 sep = ttk.Separator(ctlframe, orient='horizontal') sep.grid(row=r,column=0,columnspan=3, sticky="nsew") self.top.focus() self.top.wait_visibility() self.top.bind('', lambda x: self.top.destroy()) self.top.update() self.canvas.configure(scrollregion=(0,0,578,ctlframe.winfo_height())) def proc_pinbtn(self): print("pin") def getwins(self): result = subprocess.run(["/usr/bin/wmctrl", "-lGpx"],capture_output=True,text=True) winsinfo = [] for line in result.stdout.splitlines(): wininfo = line.split(None, 9) winsinfo.append((wininfo[0],wininfo[9])) return winsinfo def findwin(self,win_name): allwins = self.getwins() for window_id, win_name in allwins: if progname in win_name: return window_id return False def bringtoactive(self,win_id): todesk = self.getactive() subprocess.run(["/usr/bin/vdesk", todesk, str(win_id)]) def pintozero(self,win_id): subprocess.run(["/usr/bin/vdesk", "0", str(win_id)]) def getactive(self): result = subprocess.run(["/usr/bin/vdesk"],capture_output=True,text=True) return result.stdout root = Tk() app = Window(root) root.wm_title(progname) root.geometry(progsize) root.mainloop()