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, [email protected]
# 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('<Escape>', 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()