[uniquename] - 2013aug08 In a 'done-and-planned' contributions section near the bottom of my 'bio' page at [uniquename], I had a project listed to build a Tk GUI for mapping mouse buttons --- in particular, a Tk 'wrapper' for the 'xinput' command (on Linux/Unix/BSD systems). I had that 'to-do' item in the 'CSU' group, where 'SU' denotes 'Selector Utilities'. I have addressed that item on my 'to-do' list, and the code and sample image(s) are below. In the process of creating the GUI (and in the process of re-mapping some buttons on a couple of my computer mice), I found that I needed to implement several variations of the 'xinput' command: * xinput list * xinput test * xinput get-button-mappings * xinput set-button-mappings The controls for re-mapping 'pointer device' functions to the various 'buttons' (or 'protuberances' or 'wires') of the device evolved during the coding and testing, and I ended up with the GUI seen in the following image. [tkPointerSettings_grayish_screenshot_908x516.jpg] In this image, the labels colored red draw attention to the items that need to be addressed (or used) in re-mapping 'wires' of a 'pointer device'. And the green label indicates the output from the 'xinput list' command that executes when the GUI is started up. ------ Some of the typical uses of this GUI are ** To switch the functions of mouse buttons 1 and 3, for left-handed users. ** To deactivate the 'paste' function of mouse-button 2 --- for users of a mouse with scroll-wheel --- when pressing down too hard on the scroll-wheel causes an unwanted paste operation. ** To deactivate the 'scroll' functions of the scroll-wheel --- so that the wheel can be used just for the 'paste' function, without accidentally scrolling a window while doing the paste. ** To assign buttons 6 and/or 7 (on a scroll-wheel mouse with extra buttons) so that the 'paste' operation can be done with one or both of the additional buttons. (For a scroll-wheel mouse whose scroll-wheel can be tilted left or right and thus send signals using 'buttons' 6 and 7, one might assign buttons 8 and/or 9 the 'paste' function.) ------ The 'Help' button on the GUI provides pretty complete and detailed help for using the GUI. Here is the main 're-mapping guidance' from the Help: '''GUI FEATURES and OPERATION NOTES:''' The **DeviceID ENTRY FIELD** is used to specify a device number for the user's pointer device. The DeviceID can often be determined simply by looking at the **output of the 'xinput list' command**, which is shown in a **SCROLLABLE TEXT** area on the GUI. Sometimes it is not enough to look for a string like 'Type is MOUSE' or 'Type is TOUCHPAD' in the text area to find the DeviceID number after an 'id=' string. And sometimes there is not a distinguishing 'device name', like 'USB Optical Mouse' nor a manufacturer name like 'Logitech' followed by a word like 'Mouse'. Also, if the user plans to assign a function to a button (or other 'protrusion' or 'wire' of the pointer device), it may not be clear what number that 'button' has been assigned. In those cases, it is helpful to put a GUESS at a DeviceID number in the DeviceID entry field, and click on the ** 'Test' BUTTON **. Then a window pops up and for any motion of the mouse/pointer-device and for any action, like a button press, text will appear in the 'Test' window to indicate the event. Button presses, anywhere on the desktop, will show a button number. Hence you can determine button numbers for 'protuberances' on the pointer device. Once you determine the DeviceID and have it entered in the DeviceID field, you can press the ** 'GETmap' BUTTON ** and the command xinput get-button-map will be run and the output will show up in the text area to the right of the 'Map-list' label on the GUI. To change a number in the 'Map-list' string, click on a RADIOBUTTON of the GUI, to choose a position number, from 1 to 24. Then click on a line of the Functions LISTBOX, to assign that function to the chosen position in the Map-list string. The Map-list should immediately update on the GUI. For example, a left-handed user might click on RADIOBUTTON 1 and click on 'functionForButton3(menu)' in the LISTBOX. Then they would click on RADIOBUTTON 3 and click on 'functionForButton1(select)' in the LISTBOX. When the user has set the Map-list string as desired, they click on the ** 'SETmap' BUTTON ** on the GUI, which runs the command xinput set-button-map . The change to the actions of the pointer device should take effect immediately. The user can test this out by doing 'paste' operations or 'select' operations (or whatever) from/to/in various X-windows of the user's desktop. ------ '''The code''' Below, I provide the Tk script code for this 'tkPointerSettings' utility. I follow my usual 'canonical' structure for Tk code for this Tk script: 0) Set general window & widget parms (win-name, win-position, win-color-scheme, fonts, widget-geometry-parms, win-size-control, text-array-for-labels-etc). 1a) Define ALL frames (and sub-frames, if any). 1b) Pack ALL frames and sub-frames. 2) Define & pack all widgets in the frames, frame by frame. Within each frame, define ALL the widgets. Then pack the widgets. 3) Define keyboard and mouse/touchpad/touch-sensitive-screen action BINDINGS, if needed. 4) Define PROCS, if needed. 5) Additional GUI initialization (typically with one or more of the procs), if needed. This Tk coding structure is discussed in more detail on the page [A Canonical Structure for Tk Code --- and variations]. This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use). I call your attention to step-zero. One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future. ------ '''Experimenting with the GUI''' As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets. That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window. I think that I have used a nice choice of the 'pack' parameters. The labels and buttons and radiobuttons stay fixed in size and relative-location as the window is re-sized --- while the scrollable listbox and scrollable text widgets (along with their x and y scrollbars) expand/contract appropriately if the window is re-sized. You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want. ___ In addition, you might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families. In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing. I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well. ------ '''Some features in the code''' That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing. You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do. The main procs involve use of the 'xinput' command. Here are the proc names and the form of the 'xinput' command that is executed --- preceded by the button name that calls on the proc. For the 'SETmap' button, the proc 'xinput_setmap' executes: exec /bin/sh -c "/usr/bin/xinput set-button-map $deviceID $NUMSstring" For the 'GETmap' button, the proc 'xinput_getmap' executes: set NUMSstring [exec /usr/bin/xinput get-button-map $deviceID] For the 'Test' button, the proc 'xinput_test' executes: exec xterm -hold -fg white -bg black -e \ /usr/bin/xinput test $deviceID And 'xinput list' is executed within the proc 'init_xinput_parms_array', which is called in the 'Additional GUI Initialization' section at the bottom of the script. The proc 'rebuild_xinput_parms' is executed whenever a line in the listbox is clicked --- to rebuild the 'Map-list' text string of integers that is shown on the GUI. ----- It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this. Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of dogs-gone-wild, chasing their own tails. ------ <>Code for the Tk script 'tkPointerSettings.tk' : ====== #!/usr/bin/wish -f ## ## Tk SCRIPT NAME: tkPointerSettings.tk ## ##+####################################################################### ## PURPOSE: This Tk GUI script provides a GUI wrapper for the ## 'xinput' command --- to use 'xinput' to change (or ## assign) functions of buttons of a mouse or other ## 'pointer' device. ## ## The GUI supplies radiobuttons and other widgets that ## allow the user to change functions of (or assign functions ## to) the various buttons/'features' of a 'pointer device'. ## ## Here 'feature' is meant to represent an action that ## is performed by other than a button press --- such as ## a tilt (right or left) of the scroll-wheel. ## ## And 'pointer device' is meant to convey that this ## wrapper script can apply to devices other than computer ## mice --- devices such as touchpads or game 'pad' controllers. ## ## Some of the typical uses of this GUI are ## 1) To switch the functions of mouse buttons 1 and 3, ## for left-handed users. ## 2) To deactivate the 'paste' function of mouse-button 2, ## for users of a mouse with scroll-wheel, when pressing ## down too hard on the scroll-wheel causes an unwanted ## paste operation. ## 3) To deactivate the 'scroll' functions of the scroll-wheel, ## so that the wheel can be used just for the 'paste' function, ## without accidentally scrolling a window while doing the paste. ## 4) To assign buttons 6 and/or 7 (on a scroll-wheel mouse with ## extra buttons) so that the 'paste' operation can be ## done with one or both of the additional buttons. ## ##+########### ## GUI DESIGN: ## ## The GUI includes ## ## 1) a SCROLLING LISTBOX from which to choose a function to assign ## to a button/'feature' of the 'pointer device' ## ## 2) a set of at least 24 RADIOBUTTONS, to represent the ## buttons/'features' of the pointer device ## ## 3) a TEXT widget in which to show the current parameter settings for ## the 'xinput' command ## ## 4) another SCROLLABLE TEXT widget, in which to show the results ## of an initial 'xinput list' query ## ## 4) BUTTONS, such as 'Exit', 'Help', and 'SetMap'. Clicking on the ## 'SetMap' button causes the 'xinput' command to execute, using ## the current button-assignment settings. ## ##+######################################################################## ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name,win-position,win-color-scheme, ## fonts,widget-geom-parameters,win-size-control,text-for-labels-etc). ## 1a) Define ALL frames (and sub-frames). ## 1b) Pack ALL frames and sub-frames. ## 2) Define & pack all widgets in the frames, frame by frame. ## ## 3) Define key and mouse/touchpad/touch-sensitive-screen action ## BINDINGS, if needed. ## 4) Define PROCS, if needed. ## 5) Additional GUI INITIALIZATION (typically with one or two procs ## from section 4), if needed. ## ##+################################# ## Some detail on the code structure of this particular Tk script: ## ## 1a) Define ALL frames: ## ## Top-level : 'fRleft' and 'fRright' ## ## Sub-frames of 'fRleft': none, just one listbox with scrollbar(s) ## ## Sub-frames of 'fRright' (top to bottom): ## ## - 'fRbuttons' for buttons: Exit, Help, SETmap, GETmap, Test, ColorMe ## - 'fRradbutts0' for a label for radiobuttons ## - 'fRradbutts1' for about 8 radiobuttons ## - 'fRradbutts2' for about 8 more radiobuttons ## - 'fRradbutts3' for about 8 more radiobuttons ## - 'fRxinputparms' for a text widget (holding 'xinput' parms) ## - 'fRxinputlist' for a scrollable text widget (holding ## output from 'xinput list') ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, top-to-bottom order: ## ## 3) Define bindings: none currently ## ## 4) Define procs: ## ## - 'loadfuncs2listbox' - for loading a set of GUI initialization ## ## - 'show-select_func_in_listbox' - for a binding on the listbox ## ## - 'xinput_setmap' - to 'exec' an 'xinput' command, when the ## 'SetMap' button is clicked ## ## - 'getset_bkgdcolor' - for ColorMe button (gets Rpal255,Gpal255,Bpal255 via ## a separate color-selectorGUI with 3 slider ## bars, then sets the GUI 'palette') ## ## 5) Additional GUI initialization: run 'loadfuncs2listbox' ## ##+####################################################################### ## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala') ## ## $ wish ## % puts "$tcl_version $tk_version" ## ## showed ## 8.5 8.5 ## but this script should work in most previous 8.x versions, and probably ## even in some 7.x versions (if font handling is made 'old-style'). ##+######################################################################## ## MAINTENANCE HISTORY: ## Started by: Blaise Montandon 2013aug06 Started development, on Ubuntu 9.10, ## for personal use and for donation ## of this script to wiki.tcl.tk. ## Changed by: Blaise Montandon 2013aug25 Set aRfuncs4butts array in proc ## 'init_xinput_parms_array' ## manually, for a Logitech M500 ## corded mouse to change buttons 8 ## and 9 to paste and de-activate ## the paste function of button 2. ##+####################################################################### ##+####################################################################### ## Set WINDOW TITLES and POSITION. ##+####################################################################### wm title . "tkPointerSettings - a 'wrapper' around the 'xinput' command" wm iconname . "tkPointer" wm geometry . +15+30 ##+####################################################################### ## SET COLOR SCHEME (palette) FOR THE WINDOW. ##+####################################################################### ## and ##+####################################################################### ## SET BACKGROUND COLOR vars FOR WIDGETS, like ENTRY & LISTBOX widgets. ##+####################################################################### ## Set a Gray palette for the GUI. ## set Rpal255 210 set Gpal255 210 set Bpal255 210 set COLOR_pal [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255] tk_setPalette $COLOR_pal set listboxBKGD "#f0f0f0" set entryBKGD "#f0f0f0" set radbuttBKGD "#f0f0f0" set hiliteBKGD "#ff0000" set hilite2BKGD "#00ff00" ##+####################################################################### ## SET FONT VARS for use in the 'font create' statements below. ##+####################################################################### set guiFONTsize 14 set guiFONT_SMALLsize 12 ## For variable width: set FONT_varwidth \ " -family {comic sans ms} -size -$guiFONTsize -weight bold -slant roman " set FONT_SMALL_varwidth \ " -family {comic sans ms} -size -$guiFONT_SMALLsize -weight normal -slant roman " ## For fixed width: set FONT_fixedwidth \ " -family {dejavu sans mono} -size -$guiFONTsize -weight bold -slant roman " set FONT_SMALL_fixedwidth \ " -family {dejavu sans mono} -size -$guiFONT_SMALLsize -weight normal -slant roman " ##+##################################################################### ## DEFINE (temporary) FONT-NAMES using 'font create'. ## The font names are to be used in '-font' widget specs below --- ##+##################################################################### eval font create fontTEMP_button $FONT_varwidth eval font create fontTEMP_label $FONT_varwidth eval font create fontTEMP_varwidth $FONT_varwidth eval font create fontTEMP_entry $FONT_fixedwidth eval font create fontTEMP_listbox $FONT_fixedwidth eval font create fontTEMP_text $FONT_fixedwidth # eval font create fontTEMP_msg $FONT_fixedwidth eval font create fontTEMP_SMALL_button $FONT_SMALL_varwidth eval font create fontTEMP_SMALL_label $FONT_SMALL_varwidth eval font create fontTEMP_SMALL_varwidth $FONT_SMALL_varwidth eval font create fontTEMP_SMALL_entry $FONT_SMALL_fixedwidth eval font create fontTEMP_SMALL_listbox $FONT_SMALL_fixedwidth eval font create fontTEMP_SMALL_text $FONT_SMALL_fixedwidth # eval font create fontTEMP_SMALL_msg $FONT_SMALL_fixedwidth ##+####################################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. padx,pady for Buttons) ##+####################################################################### ## For BUTTON widgets: set fePADY_button 0 set fePADX_button 0 set feBDwidth_button 2 ## For LABEL widgets: set fePADY_label 0 set fePADX_label 0 set feBDwidth_label 2 ## For RADIOBUTTON widgets: set fePADY_radbutt 0 set fePADX_radbutt 0 set feBDwidth_radbutt 2 ## For LISTBOX widgets: set feBDwidth_listbox 2 ## For ENTRY widgets: set feBDwidth_entry 2 ## For TEXT and MESSAGE widgets: set feBDwidth_text 2 # set feBDwidth_msg 2 ##+####################################################################### ## SET the top window MIN-SIZE (roughly). ## ## For width: on the LEFT SIDE of the GUI, allow for ## about 20 chars for the button-function names in the listbox. ## and, on the RIGHT SIDE of the GUI, allow for ## the minwidth of the '.fRbuttons' frame: ## at least 4 buttons (Exit,Help,SetMap,...,ColorMe). ## ## For height: allow about 20 chars high --- so that at least 20 function ## names show in the listbox. ##+####################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "123456789012345 Exit Help SetMap ColorMe"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 6 widgets x 4 pixels/widget for borders/padding ## for 6 widgets --- listbox, 4 or 5 buttons. set minWinWidthPx [expr {32 + $minWinWidthPx}] ## MIN HEIGHT --- allow for about 20 chars high in the listbox: set charHeightPx [font metrics fontTEMP_fixedwidth -linespace] set minWinHeightPx [expr { 20 * $charHeightPx}] ## Add about 28 pixels for top-bottom window decoration, and ## about 6 pixels for the listbox widget's vertical borders/padding. set minWinHeightPx [expr {$minWinHeightPx + 34}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We allow the window to be resizable and we pack the listbox ## (and a scrollable text widget) with '-fill both' so that the ## listbox (and scrollable text widget) can be enlarged by ## enlarging the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+#################################################################### ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+#################################################################### ## if { "$VARlocale" == "en"} set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonSETMAP) "SETmap" set aRtext(buttonGETMAP) "GETmap" set aRtext(buttonTEST) "Test" set aRtext(buttonCOLOR) "ColorMe" set aRtext(labelDEVICEID) "Device ID:" set aRtext(labelFUNCS) "Functions to map to the numbers" set aRtext(labelRADBUTTS) " Choose a 'button'-number to map next:" set aRtext(labelXINPARMS) "Map-list for 'SETmap':" set aRtext(labelXINLIST) \ "'xinput list' output: (These are devices discovered on this host. Note 'Type is MOUSE', etc.)" ## END OF if { "$VARlocale" == "en"} ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES: ## ## Top-level : 'fRleft' and 'fRright' ## ## Sub-frames of 'fRleft': none, just one listbox with scrollbar(s) ## ## Sub-frames of 'fRright' (top to bottom): ## ## - 'fRbuttons' ## - 'fRradbutts0' ## - 'fRradbutts1' ## - 'fRradbutts2' ## - 'fRradbutts3' ## - 'fRxinputparms' ## - 'fRxinputlist' ##+#################################################################### ##+#################################################################### ## FOR TESTING of expansion of frames (esp. during window expansion): # set feRELIEF_frame raised # set feBDwidth_frame 2 set feRELIEF_frame flat set feBDwidth_frame 0 frame .fRleft -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRbuttons \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRradbutts0 \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRradbutts1 \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRradbutts2 \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRradbutts3 \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRxinputparms \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame frame .fRright.fRxinputlist \ -relief $feRELIEF_frame -borderwidth $feBDwidth_frame ##+######################################################## ## PACK *ALL* the FRAMES. ##+######################################################## pack .fRleft \ -side left \ -anchor nw \ -fill both \ -expand 0 pack .fRright \ -side left \ -anchor nw \ -fill both \ -expand 1 ## PACK the subframes of '.fRright'. pack .fRright.fRbuttons \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRradbutts0 \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRradbutts1 \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRradbutts2 \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRradbutts3 \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRxinputparms \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRright.fRxinputlist \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################################################ ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE 'fRleft' frame -- DEFINE 1 listbox widget, ## with vertical scrollbar. ##+######################################################## label .fRleft.labelFUNCS \ -text "$aRtext(labelFUNCS)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -bg "$hiliteBKGD" \ -relief flat \ -bd 0 listbox .fRleft.listbox \ -width 20 \ -height 25 \ -font fontTEMP_listbox \ -relief raised \ -borderwidth 2 \ -state normal \ -yscrollcommand ".fRleft.scrbary set" \ -xscrollcommand ".fRleft.scrbarx set" \ # -width 0 \ # -height 0 \ scrollbar .fRleft.scrbary \ -orient vertical -command ".fRleft.listbox yview" scrollbar .fRleft.scrbarx \ -orient horizontal -command ".fRleft.listbox xview" ## Pack the widgets in the 'fRleft' frame. pack .fRleft.labelFUNCS \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRleft.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRleft.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRleft.listbox \ -side left \ -anchor nw \ -fill both \ -expand 1 ##+############################################################# ## START DEFINING WIDGETS and PACKING in THE *RIGHT-SIDE* FRAME: ##+############################################################# ##+########################################################## ## IN THE 'fRright.fRbuttons' frame -- ## DEFINE several BUTTONS ## --- Exit, Help, SetMap, ... , ColorMe. ##+########################################################## button .fRright.fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {exit} button .fRright.fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext"} button .fRright.fRbuttons.buttSETMAP \ -text "$aRtext(buttonSETMAP)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {xinput_setmap} button .fRright.fRbuttons.buttGETMAP \ -text "$aRtext(buttonGETMAP)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {xinput_getmap} button .fRright.fRbuttons.buttTEST \ -text "$aRtext(buttonTEST)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {xinput_test} button .fRright.fRbuttons.buttCOLOR \ -text "$aRtext(buttonCOLOR)" \ -font fontTEMP_button \ -padx $fePADX_button \ -pady $fePADY_button \ -relief raised \ -bd $feBDwidth_button \ -command {getset_bkgdcolor} label .fRright.fRbuttons.labelDEVICEID \ -text "$aRtext(labelDEVICEID)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -bg "$hiliteBKGD" \ -relief flat \ -bd $feBDwidth_label set deviceID "?" entry .fRright.fRbuttons.entDEVICEID \ -textvariable deviceID \ -bg $entryBKGD \ -font fontTEMP_entry \ -width 2 \ -relief sunken \ -bd $feBDwidth_entry ## Label Widget on which to post some radbutts-info. # label .fRright.fRbuttons.labelINFO \ # -text "$aRtext(labelINFO)" \ # -font fontTEMP_label \ # -justify left \ # -anchor w \ # -relief flat \ # -bd 0 ##+########################################## ## Pack the widgets in the 'fRbuttons' frame. ##+########################################## pack .fRright.fRbuttons.buttEXIT \ .fRright.fRbuttons.buttHELP \ .fRright.fRbuttons.buttSETMAP \ .fRright.fRbuttons.buttGETMAP \ .fRright.fRbuttons.buttTEST \ .fRright.fRbuttons.buttCOLOR \ .fRright.fRbuttons.labelDEVICEID \ .fRright.fRbuttons.entDEVICEID \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRradbutts0' frame -- ## DEFINE a LABEL widget. ##+######################################################## label .fRright.fRradbutts0.labelINFO \ -text "$aRtext(labelRADBUTTS)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -bg "$hiliteBKGD" \ -relief flat \ -bd 0 pack .fRright.fRradbutts0.labelINFO \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRradbutts1' frame -- ## DEFINE about 8 RADIOBUTTON widgets, ## with varname 'positionIDX'. ##+######################################################## set spacerPx 150 frame .fRright.fRradbutts1.spacer \ -width $spacerPx \ -relief flat \ -bd 0 ## We will set 'positionIDX' (the radiobuttons variable) in ## the additional-GUI-init section at the bottom of this script. ## # set positionIDX 2 radiobutton .fRright.fRradbutts1.radbutt01 \ -text "01" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "1" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt02 \ -text "02" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "2" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt03 \ -text "03" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "3" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt04 \ -text "04" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "4" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt05 \ -text "05" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "5" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt06 \ -text "06" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "6" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt07 \ -text "07" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "7" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts1.radbutt08 \ -text "08" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "8" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 ## Pack the widgets in frame 'fRradbutts1'. pack .fRright.fRradbutts1.spacer \ .fRright.fRradbutts1.radbutt01 \ .fRright.fRradbutts1.radbutt02 \ .fRright.fRradbutts1.radbutt03 \ .fRright.fRradbutts1.radbutt04 \ .fRright.fRradbutts1.radbutt05 \ .fRright.fRradbutts1.radbutt06 \ .fRright.fRradbutts1.radbutt07 \ .fRright.fRradbutts1.radbutt08 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRradbutts2' frame -- ## DEFINE about 8 MORE RADIOBUTTON widgets, ## with varname 'positionIDX'. ##+######################################################## frame .fRright.fRradbutts2.spacer \ -width $spacerPx \ -relief flat \ -bd 0 ## We will set 'positionIDX' (the radiobuttons variable) in ## the additional-GUI-init section at the bottom of this script. ## # set positionIDX 2 radiobutton .fRright.fRradbutts2.radbutt09 \ -text "09" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "9" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt10 \ -text "10" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "10" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt11 \ -text "11" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "11" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt12 \ -text "12" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "12" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt13 \ -text "13" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "13" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt14 \ -text "14" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "14" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt15 \ -text "15" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "15" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts2.radbutt16 \ -text "16" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "16" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 ## Pack the widgets in frame 'fRradbutts2'. pack .fRright.fRradbutts2.spacer \ .fRright.fRradbutts2.radbutt09 \ .fRright.fRradbutts2.radbutt10 \ .fRright.fRradbutts2.radbutt11 \ .fRright.fRradbutts2.radbutt12 \ .fRright.fRradbutts2.radbutt13 \ .fRright.fRradbutts2.radbutt14 \ .fRright.fRradbutts2.radbutt15 \ .fRright.fRradbutts2.radbutt16 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRradbutts3' frame -- ## DEFINE about 8 MORE RADIOBUTTON widgets, ## with varname 'positionIDX'. ##+######################################################## frame .fRright.fRradbutts3.spacer \ -width $spacerPx \ -relief flat \ -bd 0 ## We will set 'positionIDX' (the radiobuttons variable) in ## the additional-GUI-init section at the bottom of this script. ## # set positionIDX 2 radiobutton .fRright.fRradbutts3.radbutt17 \ -text "17" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "17" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt18 \ -text "18" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "18" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt19 \ -text "19" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "19" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt20 \ -text "20" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "20" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt21 \ -text "21" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "21" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt22 \ -text "22" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "22" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt23 \ -text "23" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "23" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 radiobutton .fRright.fRradbutts3.radbutt24 \ -text "24" \ -font fontTEMP_button \ -anchor w \ -variable positionIDX \ -value "24" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd 0 ## Pack the widgets in frame 'fRradbutts3'. pack .fRright.fRradbutts3.spacer \ .fRright.fRradbutts3.radbutt17 \ .fRright.fRradbutts3.radbutt18 \ .fRright.fRradbutts3.radbutt19 \ .fRright.fRradbutts3.radbutt20 \ .fRright.fRradbutts3.radbutt21 \ .fRright.fRradbutts3.radbutt22 \ .fRright.fRradbutts3.radbutt23 \ .fRright.fRradbutts3.radbutt24 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRxinputparms' frame -- ## DEFINE a TEXT area --- to hold the current parameter ## settings for use in the 'xinput' command. ##+######################################################## label .fRright.fRxinputparms.labelXINPARMS \ -text "$aRtext(labelXINPARMS)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -bg "$hiliteBKGD" \ -relief flat \ -bd 0 text .fRright.fRxinputparms.txtXINPARMS \ -font fontTEMP_SMALL_text \ -height 1 \ -width 72 \ -wrap none \ -relief raised \ -borderwidth $feBDwidth_text ## Pack the widget in frame '.fRright.fRxinputparms'. pack .fRright.fRxinputparms.labelXINPARMS \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRright.fRxinputparms.txtXINPARMS \ -side left \ -anchor w \ -fill x \ -expand 0 ##+######################################################## ## IN THE 'fRright.fRxinputlist' frame -- ## DEFINE a SCROLLABLE TEXT area --- to hold the output ## from the 'xinput list' command. ##+######################################################## label .fRright.fRxinputlist.labelXINLIST \ -text "$aRtext(labelXINLIST)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -bg "$hilite2BKGD" \ -relief raised \ -bd 2 text .fRright.fRxinputlist.txtXINLIST \ -font fontTEMP_SMALL_text \ -height 9 \ -width 38 \ -wrap none \ -relief raised \ -borderwidth 4 \ -yscrollcommand ".fRright.fRxinputlist.scrbary set" \ -xscrollcommand ".fRright.fRxinputlist.scrbarx set" scrollbar .fRright.fRxinputlist.scrbary \ -orient vertical \ -command ".fRright.fRxinputlist.txtXINLIST yview" scrollbar .fRright.fRxinputlist.scrbarx \ -orient horizontal \ -command ".fRright.fRxinputlist.txtXINLIST xview" ##+######################################################## ## PACK the widgets in the '.fRright.fRxinputlist' frame. ##+######################################################## pack .fRright.fRxinputlist.labelXINLIST \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRright.fRxinputlist.scrbary \ -side right \ -anchor center \ -fill y \ -expand 0 pack .fRright.fRxinputlist.scrbarx \ -side bottom \ -anchor center \ -fill x \ -expand 0 pack .fRright.fRxinputlist.txtXINLIST \ -side top \ -anchor n \ -fill both \ -expand 1 ##+####################################################################### ## GET AN INITIAL 'xinput list' OUTPUT, to show in a scrollable text widget. ##+####################################################################### set xinputLISTtext [exec /usr/bin/xinput list] ##+####################################################### ## Load the 'txtXINLIST' widget using the 'insert' command. ##+####################################################### .fRright.fRxinputlist.txtXINLIST insert end "$xinputLISTtext" ##+###################################################### ## Keep the user from changing the output from the ## 'xinput list' command, in case they type in the widget. ##+###################################################### .fRright.fRxinputlist.txtXINLIST configure -state disabled ##+##################################################################### ## END OF MAIN SECTION TO SETUP THE GUI. ##+##################################################################### ##+##################################################################### ##+##################################################################### ## DEFINE BINDINGS: ## - Run the 'process_listbox_select' proc whenever ## there is a button1-release on the listbox. ##+##################################################################### bind .fRleft.listbox "process_listbox_select 0" ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## ## 'loadfuncs2listbox' - called in the 'additional-GUI-initialization' ## section at the bottom of this script. ## ## 'process_listbox_select' - called by a binding on the listbox ## ## 'init_xinput_parms_array' - called by button1-release on a ## Device-ID entry field (?) ## ## 'rebuild_xinput_parms' - called by the 'process_listbox_select' proc ## ## 'getset_bkgdcolor' - called by the Color button ## (gets Rpal255,Gpal255,Bpal255 via a GUI with 3 slider bars, ## then calls 'set_tkPalette') ## ## 'popup_msgVarWithScroll' - to show Help text when 'Help' button is clicked ## ##+##################################################################### ##+##################################################################### ##+##################################################################### ## proc 'loadfuncs2listbox' ##+##################################################################### ## PURPOSE: Loads names for pointer-device functions to listbox. ## ## ## CALLED BY: Done once, at GUI initialization, at bottom of this script. ##+##################################################################### proc loadfuncs2listbox { } { ## Make sure the listbox is empty. .fRleft.listbox delete 0 end ## Insert each function name into the listbox list. .fRleft.listbox insert end "NoOperation" .fRleft.listbox insert end "functionOfButton1(select)" .fRleft.listbox insert end "functionOfButton2(paste)" .fRleft.listbox insert end "functionOfButton3(menu)" .fRleft.listbox insert end "functionOfButton4(scrollUp)" .fRleft.listbox insert end "functionOfButton5(scrollDown)" .fRleft.listbox insert end "functionOfButton6" .fRleft.listbox insert end "functionOfButton7" .fRleft.listbox insert end "functionOfButton8" .fRleft.listbox insert end "functionOfButton9" .fRleft.listbox insert end "functionOfButton10" .fRleft.listbox insert end "functionOfButton11" .fRleft.listbox insert end "functionOfButton12" .fRleft.listbox insert end "functionOfButton13" .fRleft.listbox insert end "functionOfButton14" .fRleft.listbox insert end "functionOfButton15" .fRleft.listbox insert end "functionOfButton16" .fRleft.listbox insert end "functionOfButton17" .fRleft.listbox insert end "functionOfButton18" .fRleft.listbox insert end "functionOfButton19" .fRleft.listbox insert end "functionOfButton20" .fRleft.listbox insert end "functionOfButton21" .fRleft.listbox insert end "functionOfButton22" .fRleft.listbox insert end "functionOfButton23" .fRleft.listbox insert end "functionOfButton24" } ## END OF PROC 'loadfuncs2listbox' ##+################################################################ ## proc 'process_listbox_select' ##+################################################################ ## PURPOSE: Uses the listbox selection (and the current value ## of the 'positionIDX' radiobuttons variable) to update ## the text in the 'fRxinputparms.txtLIST' text widget. ## ## CALLED BY: bindings (above) on the listbox ##+################################################################ proc process_listbox_select {x} { global positionIDX set sel_index [ .fRleft.listbox curselection ] # if { $sel_index != "" } { # set sel_func [ .fRleft.listbox get $sel_index ] # } ## FOR TESTING: # puts "process_listbox_select: sel_func = $sel_func" ## Rebuild the string for the 'fRxinputparms.txtXINPARMS' text widget ## and load the string into the 'fRxinputparms.txtXINPARMS' text widget ## using the 'insert' command. .fRright.fRxinputparms.txtXINPARMS delete 1.0 end .fRright.fRxinputparms.txtXINPARMS insert end \ [rebuild_xinput_parms $sel_index] ## FOR TESTING: # puts "process_listbox_select: sel_index = $sel_index" } ## END OF PROC 'process_listbox_select' ##+################################################################ ## proc 'init_xinput_parms_array' ##+################################################################ ## PURPOSE: Loads the array 'aRfuncs4butts' that ## holds the current mapping of function-numbers to ## button-numbers --- and ## builds the text string for the 'fRxinputparms.txtLIST' ## text widget, and inserts the string in the widget. ## ## CALLED BY: button1-release on the DeviceID entry field (?) ################################################################### proc init_xinput_parms_array {} { global aRfuncs4butts NUMSstring # global deviceID ## The following simple loop may be replaced by an ## 'xinput get-button-map $deviceID' query ## to initialize this array according to current ## settings for the user-specified device. for {set k 1} {$k <= 24} {incr k} { set aRfuncs4butts($k) "$k" } ####################################################### ## Override the sequential setting above, to ## set the 'aRfuncs4butts' array for a Logitech M500 ## corded mouse or Logitech M510 wireless mouse or other ## similar mice --- to change buttons 8 and 9 to paste, ## and de-activate the paste function of button 2. ####################################################### if {0} { set aRfuncs4butts(2) "0" set aRfuncs4butts(8) "2" set aRfuncs4butts(9) "2" } ####################################################### ## Override the sequential setting above, to ## set the 'aRfuncs4butts' array for a Logitech M500 ## corded mouse or Logitech M510 wireless mouse or other ## similar mice --- to change buttons 8 and 9 to scroll ## up and down, deactivate 4 and 5 (so that button2 ## does not scroll --- ONLY paste). ####################################################### if {1} { set aRfuncs4butts(4) "0" set aRfuncs4butts(5) "0" set aRfuncs4butts(8) "5" set aRfuncs4butts(9) "4" } ## Build a text string from the aRfuncs4butts array. set NUMSstring "" for {set k 1} {$k <= 24} {incr k} { set NUMSstring "$NUMSstring $aRfuncs4butts($k)" } ## Insert the string in the txtXINPARMS widget. .fRright.fRxinputparms.txtXINPARMS delete 1.0 end .fRright.fRxinputparms.txtXINPARMS insert end "$NUMSstring" } ## END OF PROC 'init_xinput_parms_array' ##+################################################################ ## proc 'rebuild_xinput_parms' ##+################################################################ ## PURPOSE: Uses the listbox selection index (and the current value ## of the 'positionIDX' radiobuttons variable) to update ## an entry in the xinput-parms-array, aRfuncs4butts --- ## and rebuild the text string for the 'fRxinputparms.txtLIST' ## text widget. ## ## CALLED BY: the 'process_listbox_select' proc ##+################################################################ proc rebuild_xinput_parms {func_idx} { global aRfuncs4butts positionIDX NUMSstring set aRfuncs4butts($positionIDX) "$func_idx" ## FOR TESTING: # puts "rebuild_xinput_parms:" # puts " func_idx = $func_idx" # puts " positionIDX = $positionIDX" # puts " aRfuncs4butts(positionIDX) = $aRfuncs4butts($positionIDX)" set NUMSstring "" for {set k 1} {$k <= 24} {incr k} { set NUMSstring "$NUMSstring $aRfuncs4butts($k)" } ## FOR TESTING: # puts "rebuild_xinput_parms: NUMSstring = $NUMSstring" return "$NUMSstring" } ## END OF PROC 'rebuild_xinput_parms' ##+################################################################ ## proc 'xinput_setmap' ##+################################################################ ## PURPOSE: Runs the command ## xinput set-button-map $deviceID $NUMSstring ## to reset the 'button-map' of the user-specified device ## according to the current numbers in $NUMstring. ## ## CALLED BY: the 'SetMap' button ##+################################################################ proc xinput_setmap {} { global deviceID NUMSstring ## We popup a msg-window to the user if device-ID not set. # if {"$deviceID" == "?"} if {![string is integer $deviceID]} { popup_msgVarWithScroll .topMsg "Device ID needed." return } set deviceID [string trim $deviceID] ## NO GOOD: (no error msg, but no effect) # exec /usr/bin/xinput set-button-map $deviceID $NUMSstring ## NO GOOD: (no error msg, but no effect) # exec /usr/bin/xinput set-button-map $deviceID $NUMSstring & ## The '/bin/sh -c' technique WORKS!!! exec /bin/sh -c "/usr/bin/xinput set-button-map $deviceID $NUMSstring" ## FOR TESTING: # puts "xinput_setmap > Ran the command:" # puts "/usr/bin/xinput set-button-map $deviceID $NUMSstring" } ## END OF PROC 'xinput_setmap' ##+################################################################ ## proc 'xinput_getmap' ##+################################################################ ## PURPOSE: Runs the command ## xinput get-button-map $deviceID ## to get the 'button-map' of the user-specified device. ## ## CALLED BY: the 'GetMap' button ##+################################################################ proc xinput_getmap {} { global deviceID NUMSstring ## We popup a msg-window to the user if device-ID not set. if {![string is integer $deviceID]} { popup_msgVarWithScroll .topMsg "Device ID needed." return } set deviceID [string trim $deviceID] ## Some example 'exec' code: # exec /usr/bin/sh -c "$EDITOR_text "$FULFILname" > /dev/null 2>&1" # -command "exec $feREADER_text ./dir2dir_fileman.hlp &" set NUMSstring [exec /usr/bin/xinput get-button-map $deviceID] ## FOR TESTING: # puts "xinput_setmap > Ran the command:" # puts "/usr/bin/xinput get-button-map $deviceID" ## Insert the string in the txtXINPARMS widget. .fRright.fRxinputparms.txtXINPARMS delete 1.0 end .fRright.fRxinputparms.txtXINPARMS insert end "$NUMSstring" } ## END OF PROC 'xinput_getmap' ##+################################################################ ## proc 'xinput_test' ##+################################################################ ## PURPOSE: Runs the command ## xterm -hold ... -e xinput test $deviceID ## to monitor 'events' from the user-specified device. ## ## CALLED BY: the 'Test' button ##+################################################################ proc xinput_test {} { global deviceID ## We popup a msg-window to the user if device-ID not set. if {![string is integer $deviceID]} { popup_msgVarWithScroll .topMsg "Device ID needed." return } exec xterm -hold -fg white -bg black -e \ /usr/bin/xinput test $deviceID } ## END OF PROC 'xinput_test' ##+##################################################################### ## proc 'getset_bkgdcolor' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to get an RGB triplet (Rpal255 Gpal255 Bpal255) ## via 3 RGB slider bars. ## ## Then uses 'set_palette' proc to set window color scheme. ## ## CALLED BY: .fRright.fRbuttons.buttCOLOR button ##+##################################################################### proc getset_bkgdcolor {} { global Rpal255 Gpal255 Bpal255 # global feDIR_tkguis ## FOR TESTING: # puts "Rpal255: $Rpal255" # puts "Gpal255: $Gpal255" # puts "Bpal255: $Bpal255" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $Rpal255 $Gpal255 $Bpal255] # $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \ ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" Rpal255 Gpal255 Bpal255 hexRGB ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" # puts "Rpal255: $Rpal255" # puts "Gpal255: $Gpal255" # puts "Bpal255: $Bpal255" # set hexRGB [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255] tk_setPalette "#$hexRGB" } ## END OF PROC 'getset_bkgdcolor' ##+######################################################################## ## PROC 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext } { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # bell # bell ################################################# ## Set VARwidth & VARheight from $VARtext. ################################################# ## To get VARheight, ## split at '\n' (newlines) and count 'lines'. ################################################# set VARlist [ split $VARtext "\n" ] ## For testing: # puts "VARlist: $VARlist" set VARheight [ llength $VARlist ] ## For testing: # puts "VARheight: $VARheight" ################################################# ## To get VARwidth, ## loop through the 'lines' getting length ## of each; save max. ################################################# set VARwidth 0 ############################################# ## LOOK AT EACH LINE IN THE LIST. ############################################# foreach line $VARlist { ############################################# ## Get the length of the line. ############################################# set LINEwidth [ string length $line ] if { $LINEwidth > $VARwidth } { set VARwidth $LINEwidth } } ## END OF foreach line $VARlist ## For testing: # puts "VARwidth: $VARwidth" ############################################################### ## NOTE: VARwidth works for a fixed-width font used for the ## text widget ... BUT the programmer may need to be ## careful that the contents of VARtext are all ## countable characters by the 'string length' command. ############################################################### ##################################### ## SETUP 'TOP LEVEL' HELP WINDOW. ##################################### catch {destroy $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 wm geometry $toplevName +100+50 wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10} { text $toplevName.text \ -wrap none \ -font fontTEMP_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ## Set HELPtext var. ##+######################## set HELPtext "\ \ \ \ \ \ ** HELP for this 'tkPointerSettings' Utility ** This utility provides a GUI wrapper for the 'xinput' command --- so that you can run 'xinput' in an 'assist-mode' --- to, in the end, use 'xinput' to change functions of 'buttons' of a mouse or other 'pointer' device. The GUI supplies radiobuttons, a listbox, and other widgets that allow the user to change functions of (or assign functions to) the various buttons or 'features' of a 'pointer device'. Here 'feature' is meant to represent an action that is performed by other than a button press --- such as a tilt (right or left) of the scroll-wheel. Furthermore, rolling a scroll-wheel UP corresponds to one 'button' number, while rolling the scroll-wheel DOWN corresponds to another 'button' number --- even though these actions do not really involve a 'button'. (It might be helpful to think in terms of 'wires' rather than 'buttons'.) The term 'pointer device' is meant to convey that this Tk GUI 'wrapper' script can apply to devices other than computer mice --- devices such as touchpads or game 'pad' controllers, or even a touchscreen. Some of the typical uses of this GUI are to 1) Switch the functions of mouse buttons 1 and 3, for left-handed users. 2) Deactivate the 'paste' function of mouse-button 2 --- for users of a mouse with scroll-wheel --- when pressing down too hard on the scroll-wheel causes an unwanted paste operation. 3) Deactivate the 'scroll' functions of the scroll-wheel --- so that the wheel can be used just for the 'paste' function, without accidentally scrolling a window while doing the paste. 4) Assign 'thumb' buttons 6 and/or 7 (on a scroll-wheel mouse with extra buttons) so that the 'paste' operation can be done with one or both of the additional 'thumb' buttons. (For a scroll-wheel mouse whose scroll-wheel can be tilted left or right and thus send signals --- corresponding to 'buttons' 6 and 7, one might assign 'thumb' buttons 8 and/or 9 the 'paste' function.) 5) Or perhaps a better alternative to 4, for those who like a quick swipe-and-paste operation with buttons 1 and 2, where 2 is a scroll wheel, is: Deactivate the scroll up and down functions of 'buttons' 4 and 5 (so that button 2, the scroll wheel, is just used for paste, not scroll) --- and re-assign the scroll up and down functions of the scroll wheel to side (thumb) buttons 9 and 8, respectively. (This works better for text pasting, because we need the thumb to hold the mouse down firmly while we do the paste with the index finger pressed on the scroll wheel. We do not need this precise, held-steady positioning of the mouse to do scrolling. So it is OK to let up on the mouse with the thumb, and use the thumb on buttons 8 and 9 to scroll down and up.) GUI FEATURES and OPERATION NOTES: The **DeviceID ENTRY FIELD** is used to specify a device number for the user's pointer device. The DeviceID can often be determined simply by looking at the **output of the 'xinput list' command**, which is shown in a **SCROLLABLE TEXT** area on the GUI. Sometimes it is not enough to look for a string like 'Type is MOUSE' or 'Type is TOUCHPAD' in the text area to find the DeviceID number after an 'id=' string. And sometimes there is not a distinguishing 'device name', like 'USB Optical Mouse' nor a manufacturer name like 'Logitech' followed by a word like 'Mouse'. Also, if the user plans to assign a function to a button (or other 'protrusion' or 'wire' of the pointer device), it may not be clear what number that 'button' has been assigned. In those cases, it is helpful to put a GUESS at a DeviceID number in the DeviceID entry field, and click on the ** 'Test' BUTTON **. Then a window pops up and for any motion of the mouse/pointer-device and for any action, like a button press, text will appear in the 'Test' window to indicate the event. Button presses will show a button number. Hence you can determine button numbers for 'protuberances' on the pointer devide. Once you determine the DeviceID and have it entered in the DeviceID field, you can press the ** 'GETmap' BUTTON ** and the command xinput get-button-map will be run and the output will show up in the text area to the right of the 'Map-list' label on the GUI. To change a number in the 'Map-list' string, click on a RADIOBUTTON of the GUI, to choose a position number, from 1 to 24. Then click on a line of the Functions LISTBOX, to assign that function to the chosen position in the Map-list string. The Map-list should immediately update on the GUI. For example, a left-handed user might click on RADIOBUTTON 1 and click on 'functionForButton3(menu)' in the LISTBOX. Then they would click on RADIOBUTTON 3 and click on 'functionForButton1(select)' in the LISTBOX. When the user has set the Map-list string as desired, they click on the ** 'SETmap' BUTTON ** on the GUI, which runs the command xinput set-button-map . The change to the actions of the pointer device should take effect immediately. The user can test this out by doing 'paste' operations or 'select' operations (or whatever) from/to/in various X-windows of the user's desktop. ---- That's about it for the features of the GUI. Of course, the 'Exit' button allows for exiting and closing the 'tkPointerSettings' GUI --- and the 'Help' button shows this help. The 'ColorMe' button is there just in case your friends tell you that the GUI looks 'blah'. You can change the window's 'palette' of colors to a color of their choosing. A FEW MORE MAPPING EXAMPLES: Of course, there is no point assigning functions to 'wires' if the mouse was not made to have those 'wires'. For example, the simple scroll-wheel mice usually only have 'wires' 1 through 5. Click on the listbox to choose a function for a 'button'/'wire'. If you want to disable a button, choose 'NoOperation'. For example, if you want to disable the 'paste' operation of the scroll-wheel on a scroll-wheel mouse, click on radiobutton number 2 and click on 'NoOperation' in the listbox. And, if you want to give button 8, say, the 'paste' capability, click on radiobutton number 8 and click on 'functionOfButton2(paste)' in the listbox. MAKING THE CHANGES 'PERMANENT': To make the changes permanent (for whenever you login to your computing device), you can copy and paste the 'Map-list' string into a script (or configuration file or whatever) to make the settings 'permanent'. There is plenty of information on the internet on how to make the settings 'permanent' for your particular operating system --- including a lot of information that you would best ignore. How to separate the good info from the bad? That's an art. Typically you might want to make a little two line script that executes the 'xinput set-button-map' command with your 'device NAME' (NOT id number, which might change if devices are added). Also supply the string of numbers from the 'Map-list' that worked for you. Then you can call on that script to execute ... from a login or shell 'run control' file --- such as \$HOME/.bashrc or \$HOME/.bash_aliases or \$HOME/.profile or \$HOME/.kshrc or ... where \$HOME represents your home directory. Find information for your operating system on which file is best to modify. --- Another option is to set the 'tkPointerSettings' GUI script to startup with your favorite string of numbers in the 'Map-list'. Then you can simply click the 'SETmap' button after starting up this GUI, to return to your favorite settings. And you can return to this GUI to make minor mapping changes during a session --- for example, if you wanted to disable the paste function of button-2 (a scroll wheel) for a while --- or if you wanted to disable the scroll function provided by 'wires' 4-and-5 (of the scroll wheel), for a while. You could setup this Tk script as an icon on your desktop. Then it would be simply a double-click (or right-click and choose Open) on that icon to run this script whenever you want. " ##+###################################################### ## Additional GUI INITIALIZATION: ## ## (This is for most of the APPLICATION-SPECIFIC initialization, ## such as widget VARIABLES --- NOT for widget PARAMETERS ## such as button padding or '-relief' settings.) ##+###################################################### ## Set a default value for the radiobuttons variable. set positionIDX 2 ## Load the functions-for-buttons listbox. loadfuncs2listbox ## Initialize the string in the .fRxinputparms.txtINPUT ## text widget. init_xinput_parms_array ====== <> ------ '''THE OTHER BUTTONS''' I described use of the 'SETmap', 'GETmap', and 'Test' buttons of the GUI above --- and more detail can be seen in the HELPtext variable of the code above. Of course, the 'Exit' button exits this app and closes the GUI window, and the 'Help' button shows the text of the HELPtext variable in the code above. But there is another button on the GUI --- the 'ColorMe' button. The 'ColorMe' button is not essential. It is there just in case your friends tell you that the GUI looks 'blah'. You can change the window's 'palette' of colors to a color of their choosing. Example: [tkPointerSettings_bluish_screenshot_909x514.jpg] If you want to implement that button, it uses an 'external' Tk color-selector script whose code you can get from a page that I have provided on this wiki --- at [A non-obfuscated color selector GUI]. ------ '''SOME POTENTIAL ENHANCEMENTS:''' I actually do not have any plans for further enhancements for this GUI, but I have recently purchased several 'pointer devices' --- both for testing this utility, and for replacing the mice that I use on my main desktop and netbook computers with a mouse by which I can more easily do 'text swipe-and-paste' operations. The scroll-wheels on my current mice make that nice Unix-era operation a frustration, as the paste often causes the scroll-wheel to rotate and move the text in an editor window when I did not want to scroll the text. I even bought a 'SteelSeries 3GC Controller - USB Rumble PC Controller' --- for only $20 at Walmart. This is actually a game controller, but I thought I would try testing out how it would do as a mouse replacement. If testing goes OK, I may replace the mice on my desktop and netbook computers with mice that have additional buttons that I can map to the 'paste' function. Mice like * the Logitecth M500 Corded Mouse (corded for less electro-magnetic interference) or * the Logitech M510 Wireless Mouse. I may update the code posted here if I find I need some enhancements in dealing with these 'pointer devices'. By the way, I have not tested this Tk GUI in mapping functions of a 'touchpad'. I find touchpads too counter-productive for use in code development, so I never use them. But if anyone tries using this utility for re-mapping functions of a touchpad, and if they discover any useful information, they could post their experiences here. ------ '''IN CONCLUSION''' There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never). I hope to provide more 'user-friendly, useful utilities' --- UUU's --- done with Tk scripts. ('useful' and 'utility' may seem redundant, but I think those words are worth repeating.) As I have said on several other code-donation pages on this wiki (and this also bears repeating) ... A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going. ------ [uniquename] 2013aug25 '''update''' - with info on Logitech M500 (and M510) mouse I have been using a Logitech M500 mouse for a day now with a couple of button-mapping experiments. I have found a 'pretty good' mapping to support the fast text 'swipe-with-MB1-and-paste-with-MB2' that I find so productive. You can see the couple of mappings that I have tried in the 'init_xinput_parms_array' proc in the code above. I replaced the code above --- with the changes in that proc --- and with some spelling corrections in the 'HELPtext' variable. I also added some text in the HELPtext variable that explains what I did for the Logitech M500 mouse. In the following image of the M500, you can see the two buttons on the side of the mouse (buttons 8 and 9) that I am mapping. [mouse_logitech_m500_288x284.jpg] Here is an extract from that HELPtext: ... for those who like a quick swipe-and-paste operation with buttons 1 and 2, where 2 is a scroll wheel ... : Deactivate the scroll up and down functions of 'buttons' 4 and 5 (so that button 2, the scroll wheel, is just used for paste, not scroll) --- and re-assign the scroll up and down functions of the scroll wheel to side (thumb) buttons 9 and 8, respectively. (This works better for text pasting, because we need the thumb to hold the mouse down firmly while we do the paste with the index finger pressed on the scroll wheel. We do not need this precise, held-steady positioning of the mouse to do scrolling. So it is OK to let up on the mouse with the thumb, and use the thumb on buttons 8 and 9 to scroll down and up.) This mapping --- which is '1 2 3 0 0 6 7 5 4 10 11 ...' for righties --- or '3 2 1 0 0 6 7 5 4 10 11 ...' for lefties --- would also work for the Logitech M510, which is the wireless version of the M500. This is still not an ideal situation for at least a couple of reasons: * I no longer have the 'auto-repeat' (FAST notched or smooth) scrolling that I had with the mouse wheel. * Pasting with the mouse wheel is not very satisfying because the wheel tends to roll when I press down on it. Although I have de-activated the scroll up/down function, it is not a pleasant feeling for the paste button to be rolling when I paste. I may look into using a mouse on which there are one or more extra buttons ON TOP OF the mouse, that I can make a 'paste' button. The following image shows a 'Steelseries World of Warcraft' mouse ('Cataclysm Edition') with a couple of buttons on top of the mouse in white circles. [mouse_steelseries_worldOfWarcraft_2circles_532x360.jpg] I may try that mouse and use one of those top buttons for paste --- which would allow me to use the scroll-wheel for scrolling --- instead of the side/thumb buttons that I am using for repeated-button-press scrolling on the Logitech M500. I looked for this mouse in local stores but could not find it. However, I found the Steelseries World of Warcraft '''Legendary Edition''' mouse which has a couple of buttons just behind the scroll wheel, and a button on the right of the mouse, that might be mappable to a paste operation. See the following 2 images. (Buttons 9 and 11 look like the best candidates for a paste operation. In fact, I could make both of those buttons function as 'paste' buttons. In the manual that comes with the mouse, those 2 buttons are called the 'World Map' and 'Zone Map' buttons.) [mouse_Steelseries-WorldOfWarcraft-LegendaryEdition_11buttons_300x225.jpg] [mouse_Steelseries-WorldOfWarcraft-LegendaryEditon_rightButton_400x261.jpg] And there are four buttons on the left of the 'Legendary Edition' mouse, with one of the buttons being located low enough that one might be able to press the button without moving the mouse. See the image below --- for the 4 'thumb' buttons that are labelled 1, 2, 3, and 4 in the 11-button image above. [mouse_Steelseries-WorldOfWarcraft-LegendaryEditon_leftButtons_401x159.jpg] I may try out this mouse and report back here. There are plenty of mice with 'additional top buttons' that I can try, such as the Logitech G300 or G500 or G700 gaming mice (images follow). [mouse_logitech_g300a_240x240.jpg] [mouse_logitech_g500_141x273.jpg] [mouse_logitech_g700_300x225.jpg] And there are other brands with 'additional top buttons', such as: [mouse_corsair_vengeance_m90_500x363.jpg] Corsair Vengeance M90. [mouse_razer_mamba_wireless_5600dpi_465x257.jpg] Razer Mamba wireless. [mouse_sunsonny_sm-sg80_447x379.jpg] Sunsonny SM-SG80. [mouse_zalman_zm-m300_wired_377x350.jpg] Zalman ZM-M300 wired. [mouse_CoolerMaster-Storm-Inferno_400x304.jpg] Cooler Master (CM) Storm Inferno [mouse_ShogunBrothers-Ballista-MK-1Pro_400x250.jpg] Shogun Brothers Ballista MK-1 Pro There used to be a Logitech mouse on which the button on the left side of the mouse was BELOW where the (right-handed person's) thumb rests --- the Logitech 'Mouseman Dual Optical' (image below). [mouse_logitech_MousemanDualOptical_400x232.jpg] Unfortunately, Logitech no longer makes a mouse like that. In fact, in one old web post that I saw, a fellow described trying a newer Logitech mouse, but he went back to the 'Mouseman Dual Optical' because he experienced what I experienced --- when the buttons are ABOVE the thumb, the mouse tends to move around when you move the thumb UP to press on the button. Among images of newer mice that I have seen, the following two mice may have 'thumb-buttons' low enough so that the mouse does not move around when you press on the button. [mouse_Thermaltake-TT-esports-volos_400x255.jpg] Thermaltake TT Esports Volos (You might be able to press the green button without the mouse moving.) [mouse_MadCatz-Cyborg-MMO7_400x273.jpg] Mad Catz Cyborg MMO7 (That silver object in the lower left of this image may be a button that would be positioned BELOW where the thumb usually rests. If that would not work, it looks like there are several buttons on TOP of the mouse, near the scroll wheel and at the upper left of this picture, which might be mappable to a paste operation.) If I finally settle on a mouse and mapping, then I may try experimenting with the Steelseries 3GC game controller: [game_controller_steelseries_3gc_647x515.jpg] What can I make this baby do? Control a robot through my computer --- to open the refrigerator door and bring me a beer? More later. <> GUI