Version 53 of weedesk

Updated 2004-11-01 23:58:57

if 0 {

Larry Smith

Weedesk - a wee little desktop environment in tcl/tk.


http://www.smith-house.org:8000/images/weedesk1.gif


http://www.smith-house.org:8000/images/weedesk2.gif


Weedesk is an idea I've been noodling over for several days, and the sudden appearance of Desktop Environment shows me I'm not the only one wishing for a simple, extensible, portable desktop environment.

The Desktop Environment page lists a number of applications, but all of these are designed to stand alone. Ideally apps for the wee desktop would be new and designed just for it.

In my prototype code I've decided to go for a Macintosh-like system - the menubar goes across the top of the screen and changes as you select various windows. However, I've not integrated tkMenuMgr from Menus Even Easier yet, so all there is is a place-holding button that exits the desktop.

I picture a desktop with a number of lightweight, scriptable, applications (weeApps): weeEdit is used for all text editing, weeCalc is the spreadsheet, weePage is a page layout app - takes text from weeEdit files and allows you to insert them into a web page - there would be no "weeWord", just weeEdit and weePage, which together serve the same purpose using html as an internal file format. Some other apps would be weeBrowse, weeMail, weeProg (an IDE/debugger for developing scripts) weeData (the database), weeChat, and so on. Also, the environment would support services - weeweb httpd, etc.

I visualize it running in a starkit. Installation = copy.

Here's some code to play with. Right now it's sort of an mdi on steroids. To the Internal Movable Windows code I've added various window buttons and functions (iconize not yet implemented), resizing and so on. }

 proc init { args } {
   upvar args arglist

   set rest ""
   if { [ llength $arglist ] == 0 } return
   if { [ llength $arglist ] == 1 } { eval set arglist $arglist }
   set varlist {}
   foreach { var val } $args {
     uplevel 1 set $var \{$val\}
     lappend varlist $var
   }
   foreach { var val } $arglist {
     set var [ string range $var 1 end ]
     if { [ lsearch $varlist $var ] != -1 } {
       uplevel 1 set $var \{$val\}
     } else {
       lappend rest -$var $val
     }
   }
   return $rest
 }

 proc winMove { win { stage "" } } {
   upvar _win$win offset
   upvar #0 $win-data windata

   if [ string equal $windata(state) "maxed"] return
   switch -exact $stage {
     start {
       place forget .help
       set offset(x) [winfo pointerx .]
       set offset(y) [winfo pointery .]
     } default {
       set wmX [winfo pointerx .]
       set wmY [winfo pointery .]
       set xDiff [expr {$wmX - $offset(x)}]
       set yDiff [expr {$wmY - $offset(y)}]
       array set placeInfo [place info $win]
       set windata(x) [expr {$placeInfo(-x) + $xDiff}]
       set windata(y) [expr {$placeInfo(-y) + $yDiff}]
       place $win -x $windata(x) -y $windata(y)
       place $win-tab -x $windata(x) -y $windata(y) -anchor sw
       set offset(x) $wmX
       set offset(y) $wmY
     }
   }
 }

 frame .rubberband -bd 3 -relief ridge
 set rbx ""
 set rby ""
 proc winResize { win { stage "" } } {
   global rbx rby
   upvar #0 $win-data windata

   if [ string equal $windata(state) "maxed"] return
   switch -exact $stage {
     start {
       set w [ winfo width $win ]
       set h [ winfo height $win ]
       set rbx [ winfo x $win ]
       set rby [ winfo y $win ]
       set r [ expr $rbx + $w ]
       set b [ expr $rby + $h ]
       event generate . <Motion> -warp 1 -x $r -y $b
       .rubberband configure -width $w -height $h
       place .rubberband -x $rbx -y $rby
       raise .rubberband
     } done {
       place forget .rubberband
       set basex [ winfo x . ]
       set basey [ winfo y . ]
       set w [ expr [ winfo pointerx $win ] - $basex - $rbx ]
       set h [ expr [ winfo pointery $win ] - $basey - $rby ]
       place $win -width $w -height $h
     } default {
       set basex [ winfo x . ]
       set basey [ winfo y . ]
       set w [ expr [ winfo pointerx $win ] - $basex - $rbx ]
       set h [ expr [ winfo pointery $win ] - $basey - $rby ]
       .rubberband configure -width $w -height $h
     }
   }
 }

 proc iconize { w } {
   puts "iconize"
 }

 proc winShrinkGrow { win } {
   upvar #0 $win-data windata
   if [ string equal "normal" $win-data(state) ] {
     set windata(w) [winfo width $win]
     set windata(h) [winfo height $win]
     set windata(x) [winfo x $win]
     set windata(y) [winfo y $win]
     set x 0
     set y 0
     set w [ winfo width . ]
     set h [ winfo height . ]
     place $win -x $x -y $y -width $w -height $h
     set windata(state) maxed
   } else {
     set x $windata(x)
     set y $windata(y)
     set w $windata(w)
     set h $windata(h)
     place $win -x $x -y $y -width $w -height $h
     set windata(state) normal
   }
 }

 proc winClose { win } {
   global prevwin
   upvar $win-data windata
   if [ string equal $prevwin $win ] {
     set prevwin ""
   }
   destroy $win
   destroy $win-tab
   unset windata
   place forget .help
 }

 label .help -bd 1 -fg black -bg lightyellow -font fixed -text "default help"

 set btn3 0
 proc popballoon {} {
   global btn3
   if { !$btn3 } { place forget .help }
 }

 proc help {w help} {
   bind $w <Any-Enter> "after 1000 balloon %W $help; after 3000 popballoon"
   bind $w <Any-Leave> "popballoon"
   bind $w <ButtonPress-3> "set btn3 1; balloon %W $help"
   bind $w <ButtonRelease-3> "set btn3 0; popballoon"
 }

 proc balloon { w args } {
   .help configure -text $args
   regexp {^\.[A-Za-z0-9]*} $w parent
   if { [ catch { set x [ expr [ winfo x $w ] + [ winfo x $parent ] + 10 ] } ] == 0 } {
     set y [ expr [ winfo y $w ] + [ winfo y $parent ] - 10 ]
     place .help -x $x -y $y
     raise .help
   }
 }

 proc showhideBtns { win } {
   upvar #0 $win-data windata
   if [string equal $windata(btnstate) "hidden" ] {
     # show the buttons
     pack forget $win-tab.showhide
     pack $win-tab.done -side right
     pack $win-tab.resize -side right
     pack $win-tab.iconize -side right
     pack $win-tab.grow -side right
     pack $win-tab.lower -side right
     pack $win-tab.showhide -side right
     set windata(btnstate) showing
     $win-tab.showhide configure -text "<"
   } else {
     # hide the buttons
     pack forget $win-tab.done
     pack forget $win-tab.resize
     pack forget $win-tab.iconize
     pack forget $win-tab.grow
     pack forget $win-tab.lower
     set windata(btnstate) hidden
     $win-tab.showhide configure -text ">"
   }
 }

 proc winGoto { x y win } {
   global $win-data
   place $win -x $x -y $y
   set $win-data(x) $x
   set $win-data(y) $y
   set $win-data(w) [ $win cget -width ]
   set $win-data(h) [ $win cget -height ]
 }

 proc btn { name text cmd helptext } {
   set btn [button $name -text $text -padx 1 -pady 0 -bd 1 \
             -command $cmd -cursor top_left_arrow ]
   help $btn $helptext
 }

 proc winNew {win title txt} {
   upvar #0 $win-data windata
   set windata(state) normal
   set windata(btnstate) hidden
   frame $win -bd 1 -relief raised
   frame $win-tab -bd 1 -relief raised -cursor fleur
   pack [ menubutton $win-tab.appmenu -text "<" -direction left \
     -relief raised -padx 1 -pady 0 -bd 1 -cursor top_left_arrow ] -side left
   help $win-tab.appmenu "Application Menu"
   pack [label $win-tab.title -text $title ] -side left
   help $win-tab "Click/Drag to Move"
   help $win-tab.title "Click/Drag to Move"
   btn $win-tab.done x "winClose $win" "Close & Exit"
   btn $win-tab.resize \u2198 "" "Click/Drag to Resize"
   btn $win-tab.iconize . "iconize $win" "Window->Icon"
   btn $win-tab.grow \u2195 "winShrinkGrow $win" "Grow/Shrink Window"
   btn $win-tab.lower \u2193 "lower $win" "Lower Window"
   btn $win-tab.showhide ">" "showhideBtns $win" "Show/Hide Btns"
   pack $win-tab.showhide -side right

   pack [text $win.t -width 20 -height 3] -side top -fill both -expand 1
   $win.t insert end $txt

   bind $win-tab.title <ButtonPress-1> "winMove $win start"
   bind $win-tab.title <B1-Motion> "winMove $win"
   bind $win.t <ButtonPress-1> "winSelect $win"
   bind $win-tab.resize <ButtonPress-1> "winResize $win start"
   bind $win-tab.resize <ButtonRelease-1> "winResize $win done"
   bind $win-tab.resize <B1-Motion> "winResize $win"

   return $win
 }

 set prevwin ""
 proc winSelect { win } {
   global prevwin
   upvar #0 $win-data windata

   if ![ string equal $prevwin "" ] {
     place forget $prevwin-tab
     bind $win.t <ButtonPress-1> "winSelect $win"
   }
   bind $win.t <ButtonPress-1>
   set x $windata(x)
   set y $windata(y)
   place $win-tab -x $x -y $y -anchor sw
   focus $win
   raise $win
   raise $win-tab
   set prevwin $win
 }

 pack propagate . 0
 wm focusmodel . active
 # set width [ winfo screenwidth . ]
 # set height [ winfo screenheight . ]
 set width 500
 set height 500
 . config -width $width -height $height -bg blue
 focus -force .
 # wm overrideredirect . 1
 update
 # grab -global .

 winGoto 30 200 [winNew .win1 "1st Window" "Hello World" ]
 winGoto 300 50 [winNew .win2 "2nd Window" "Hello Person" ]

 winSelect .win1

Wee - indeed! How would you want to see the task model: as procs, interps, threads, processes (connected via pipes or sockets), or some mix? -jcw

2004-1-16 SRIV: I run a minimal window manager in Linux, where most of my everyday apps are coded in tcl/tk, a menu for apps/bookmarks, network throughput, mail waiting indicator, time/date. My main editor IdenTcl is pure tcl, tkabber for chat, tkCVS, calculator, CDR-B-Q, filemanager (modified X-Files), pgAccess, SnackAmp, & a Mixer app. I'd prefer a window manager written & extensible in tcl. I was hoping one of the wm gurus would donate a modern sample for us to build upon, which would give us the ability to embed any X app into the toplevel. But, I think this would be difficult to do on other OS's. I think if I had a primitive tcl web browser I'd be ready to use weedesk in daily use, and it would be awesome on my Linux iPaq.

I'm up for further discussions... Steve

Larry Smith: Wow. Big response. =) To tell the truth, I haven't yet given much thought to the task model. Initially I was thinking in terms of procs to make implementing the mac-like menu system easier. Then I began leaning to the idea of different processes so most any program could be run under the system - though that would destroy the consistancy, which is important, I think.

wm2 is a very small window manager with a rather attractive look and feel. It is not configurable, it has no extensions capability - but it wouldn't be hard to bolt a tcl interpreter into it and use it for the chassis. The above code would be superseded, but the system would be much more flexible.

1-25-2004 MDD: I would argue strongly against that. The whole point here is that we build a Tcl-based operating environment that runs on all platform Tcl runs on. Building a windowing manager and virtual operating environment out of Tcl is vastly more interesting than just bolting a Tcl interpreter on some existing WM.

1-25-2004 SRIV: Exactly. I'd like a cross-platform wm written in tcl. Why? Because I hate using Windows. I want my wm to work the same on Linux and Windows. I'd like it easy to tweak and extend. The whole premise of weedesk being written in tcl is whats got me interested.

escargo 26 Jan 2004 - I had already found one window manager that is trying to be friendly to scripting languages, TrsWM[L1 ] (although the scripting language is Lua and not Tcl).


FW: How about BrowseX for a Tcl browser?

SRIV: I could live with that.

I figured we'd use that. To get a consistent look and feel across all apps, however, would require some interface changes, including using tkMenuMgr.


1-16-2004 MDD: Very promising start! To add my own $0.02, I'd propose that, as a rule, all weeApps, as well as the environment itself, should have to be strictly cross-platform compatible, so that if any extensions are used, they would have to be Star-packaged for seamless access on WIn, Mac and Linux. Also, any pure-tcl app should be able to run under wee with no changes required. With a soup-to-nuts kit of extensions, such as an expanded Kitten, in fact, just about any existing Tcl app should be able to be made to run under the environment. If it could be made to have a Windows look and feel, such a thing could eventually scare the pants off a certain company in Redmond, WA, btw. Also, wouldn't a wee little shamrock make a fine logo for such a wee little desktop? ;-)

2004-1-17 SRIV: I agree with MDD on all points. I envision starkits that I can keep on my USB watch that contain the cross platform desktop and apps. I use Linux, but would want to pop my watch into a customer/friends Windows box and run everything my way.

2004-1-19: Larry Smith: Yes - unquestionably it should be 100% cross-platform. I propose to follow the 80/20% rule - the apps would be simple ones, providing only the minimal functionality a casual user would need, plus a little. Power users will prefer their specialized apps, so leave them to their choices, there are plenty out there. The weedesk objective would be to provide an identical user experience on any system a casual user might need, eliminating retraining time and making their skills 100% portable.


jcw - To hijack this initiative slightly: is the time ripe for a (possibly distributed/collaborative) Tcl compile farm?

I've been focusing my build efforts on Win+Mac+Lin, though FreeBSD may creep in soon, and the above shows that ARM/iPaq Linux and/or WinCE might also need to follow. Might [L2 ] or [L3 ] (old) be relevant?

To get back on track for this page, how about: adopt above Tcl policy, and accept binary extensions only if they are provided for all platforms that have been picked out (W+M+L, maybe one or two more?). A "wee-${arch}.kit" would only contain extensions that are available for all platforms, w/o compromise, and with the same release number. Architectures can only be added after all current extensions have been built for it. Packages can only be added after all builds are done. I.e., a full matrix as requirement?

MDD - That sure would go a long way toward insuring platform integrity and stability. We should probably start another page to discuss the range of necessary packages: weePackages.

Larry Smith: good idea.

Larry Smith (2/5/04): updated the code to add some new features. The window functions can now be hidden or displayed with the "<" and ">". To conserve screen real estate only the active window gets a titlebar. There is a window menu button "<", which is intended to open the application toolbar. There is still no menubar, it is proving difficult for me to adapt tkMenuMgr from Menus Even Easier to multiple menubars selected according to the current active app. Anyway, more code to play with.

2004-02-08 SRIV: After toying with weeCalc for a day, I realize there needs to be a standard interface to exchange parameters between the desktop and the app, like the window name and window title. Also, I think namespacing will be a must for each app, especially if we share the interp. Now's the time to devise a standard way to swallow a script/starkit into a window frame.

2004-02-10 Larry Smith A lot of grief will be saved if apps are written to take the -use <windowid> option to specify their toplevel window. Then weedesk can set up the frame and simply start up another wish to run the app.

2004-02-10 SRIV: I tried that direction first but failed. I must re-read the docs.

NEM 23June2004: Note that the -use option to wish (and toplevel embedding across processes in general) is not supported on Mac OSX/Aqua and it's unlikely to be supported any time soon due to technical issues. IIRC, window ids are not portable across processes under Aqua.


06oct04 jcw - Just came across this page and all the wee* ideas again. I am looking for a way to have a single screen/window show a range of different subwindows/tasks. MDI and weedesk came to mind. But being a hopeless minimalist, I would like to keep things really really simple, i.e. devoid of visual detail. I happen to think that simple designs lead to simple and snappy implementations...

Here's an idea: a screen (weedesk) can only contain up to 4 windows. Each is constrained to a corner, i.e. it can only be resized, not moved. It can never be resized to the full height/width of the entire screen, leaving say at least 30 pixels uncovered. This guarantees that no window is ever 100% covered. It also means there is no need for more than a single drag corner to resize, and clicking on windows to bring them to the top.

To deal with more than 4 windows, multiple screens could be used (with a way to reorg which window goes where), but that's not a huge priority for simple uses. One approach would be to have a minature desktop with N small screens along say the right edge, making *every* window reachable with a single click (or some nice simple keyboard shortcuts).

Imagine having an edit, debug, run, inspect combo on a screen. Or email, browser, chat, calendar. Four windows really covers a lot of cases ...

(Just some rambles from someone who - despite being fond of the Mac & Aqua - is fed up with the movable-overlapping window metaphor. And hierarchical file systems, but that's another story...)

07oct04 jcw - See GooWee.

01Nov04 SRIV Another idea: allow GooWee or WeeDesk to have a choice of window layouts, such as MDI, Tabbed, Win9x, GooWee, etc.. This way the user can choose the mode, but the core app embedding technology can remain consistant. I think more folks would be willing to join in if they can "have it their way".

AET - Following up on JCW's dislike of hierarchical filesystems, see www.thebrain.com. The mature interface here uses a concept-mapping front-end to access ANY bit of information ANYWHERE, where you can follow links that connect bits together, making it amazingly fast to find files information. I have been using this software (PersonalBrain) for years, and it really works for me. TheBrain.com recommend that you use a single directory for all files and access them all through the graphic front-end, which is a perfectly rational approach, though not necessary, and you can superimpose PersonalBrain over another system without disturbing that system or committing to it. PersonalBrain is lacking in many areas, so I have wanted for some time to write a Tcl/Tk Megawidget that uses the concept-mapping base, allowing it to be used as a front end for (1) filesystems (2) the web (3) databases (4) knowledge management (5) . . . Coupled with Tcl/Tk, concept mapping is a certain winner. Embedded in WeeDesk, we could have a World Beater.


Category Application | Category GUI