#|
Summary


Activate buttons on motionbox without jostling it.

Toggle primary button for "press" or "release",
 f.e. drag'n drop without accidental drop.

Buttonboard bindings for numeric pad.
|#


#|
This emerges in the universe on its own, as all has.
|#


#|
Content outline


* Requirements

* General function use and naming conventions


* Commands.

 * stumpwm:defcommand pointer-button-activate.nu
    Activate the pointer button of a motionbox.
    Obsoletes #'ratclick and #'*-fake-click functions.

 * stumpwm:defcommand pointer-button-toggle.nu
    Press or release the pointer button of a motionbox.
    Specifically: drag'n drop without accidental drops.


* Buttonboard bindings for pointer activation
|#


#|#
Requirements


This was experienced within:

* stumpwm tiled-view manager
  It was a lisp image of SBCL
  (Steel Bank Common Lisp 2.1.0).
  stumpwm.github.io
  sbcl.org

* Common Lisp X Interface (CLX) 0.7.5
  The CLX had the XTEST extension (or similar)
  for activating the pointer in the Xserver.
  github.com/sharplispers/clx

* Xserver
  x.org
|#


#|
Hereafter, there be code.
|#

#|#
General function use and naming conventions


"pointer-button": Collective name for symbols.

".nu": Arbitrary suffix for preventing name collisions,
       as in "named uniquely" or "now unique".


Trace the source of variables by prefixing a letter
 representing that source.

   defun args prefix:  F for "function"
     let args prefix:  L for "let"
  lambda args prefix:  A for "anonymous function"

Combine A and L prefixes by depth, thereby revealing scope
 and discouraging perplexation.  For example:

                       defun args  ->  F prefix
                 defun > let args  ->  L prefix
  defun > let > let > lambda args  ->  LLA prefix


Favoring using the special operator 'multiple-value-call
 instead of the macro 'multiple-value-list for results
 from Common Lisp funicles returning multiple values.
Consider replacing any funicles returning multiple values
 with new funicles returning a list, thereby eliminating
 the use of 'multiple-value-call, too.


The stumpwm source tends to have variables and functions
 prefixed with an "x" when referring to an instance from
 the Common Lisp X Interface library, like an xlib class
 or structure.  For example: xwin.  Do the same.


See also:
* Notes from the documentation for stumpwm and CLX.
|#


#|
Commands.
|#


 ;#
(stumpwm:defcommand
pointer-button-activate.nu
(&optional
(Fbutton 1))
 ;;The values were ignored from the keybinding
 ;; when nothing declared for a prompt.
 ;; No prompt needed, but value type seems needed.
((:number"Pointer button to activate (1-5): "))
#|
Replace functions for activating pointer buttons.
Function stumpwm:ratclick
 > stumpwm:send-fake-click
 > stumpwm:xlib-fake-click
 > xlib:send-event
 Fails.
Consider ratclick and *fake-click as obsolete.
Use xlib/xtest:fake-button-event.
 Tested and works for button 1 (primary)
 and button 3 (secondary).
|#
"Activate the Xserver pointer button.

Fbutton is a number representing a button on a motionbox,
 typically 1 through 5.  Default is 1.

Send two events for the button to the Xserver:
 a button-press event, then a button-release event."
#|
Required by #'xlib/xtest:fake-button-event function:
 display; button number; t for press, nil for release.
|#
 ;;Press the button.
(xlib/xtest:fake-button-event stumpwm:*display* Fbutton t)
 ;;Release the button.
(xlib/xtest:fake-button-event stumpwm:*display* Fbutton()))


 ;#
(stumpwm:defcommand
pointer-button-toggle.nu
(&optional
(Fbutton 1))
((:number"Pointer button to toggle (1-5): "))
"Press or release the Xserver pointer button.

Fbutton is a number representing a button on a motionbox,
 with 1 through 5 supported by an Xserver.  Default is 1.

Release the pointer button for the cursor when it
 is already pressed, otherwise press it.
That is, it is like a toggle switch, so no need to hold
 a button, and the same command be used because it finds out
 whether that button is currently considered by the Xserver
 as pressed.

Specifically intended for drag-and-drop without needing to
 hold a button while dragging, regardless of positioning
 the cursor with a motionbox or by some other means.
In other words:
 1. Move the motionbox to position the cursor for pickup
    (or position the cursor by any other means).
 2. Type the button on the buttonboard for this command.
    No need to hold the button on buttonboard,
    and no need to use the buttocks on the rodent.
 3. Move the motionbox to reposition cursor for drop
    with no worries of accidentally releasing a button.
 4. Type the same button on the buttonboard for
    this command to complete the drag'n drop.

Be aware some computer programmers made their programs
 purposely disable the keyboard when a pointer button
 is detected as pressed, regardless of how it is toggled.
 Or, their programs might temporarily remap the keyboard
 when a pointer button is detected as pressed.
Either way, the bindings for any personalized commands
 might become inaccessible, thereby making the person
 operating the computer become artificially disabled
 by the program of those computer programmers.
In that case, either complete the drag'n drop action with
 a motionbox, or try to cancel by using the Esc button."
#|
Send a button-release event for the button to the Xserver
 when the button is known to be pressed, otherwise send
 a button-press event.

The function #'xlib:query-pointer returns multiple values,
 with the fifth element as the keyboard/button mask.
 The first byte represents the modifier buttons on
 the keyboard.  The next five bits are for the five buttons
 supported by the Xserver.  A bit as 1 for a button means
 it is currently pressed.
For example, the mask when button 1 is pressed is 256
 as a decimal number, or 00000001 00000000 as binary.
 Dropping the first 8 bits (the 00000000) leaves 00000001.
 Similarly the mask for button 3 pressed becomes 00000100.
|#
(let
( ;;;;All pointer buttons currently pressed.
 ;;;; Drop the keys from the mask.
(Lall-buttons-mask
(ash
(car
(cddddr
(multiple-value-call'list
(xlib:query-pointer
(stumpwm:screen-root(stumpwm:current-screen))))))
-8))
 ;;;;Mask for matching the chosen button.  The bit position
 ;;;; is the same as the button number.
(Lbutton-mask(ash 1(1- Fbutton))))
 ;;;Press or release the button: t to press, nil to release.
(xlib/xtest:fake-button-event
stumpwm:*display*
Fbutton
#|
Nothing matched when #'logand returns 0, so button is
 currently unpressed and needs to be pressed.
The #'zerop function returns T when the value given it
 is 0, which is also the same value needed for pressing
 the button.  Otherwise, it returns nil, which is also
 what is needed for releasing the button.
|#
(zerop(logand Lall-buttons-mask Lbutton-mask)))))


#|#
Buttonboard bindings for pointer activation
 and cursor halving.


The optional modifier buttons when binding a button:

  Control "C-"
  Meta    "M-"
  Alt     "A-"
  super   "s-" (a.k.a. Command or Windows)
  Hyper   "H-"
  Shift   "S-" (often unused, f.e. "A" instead of "S-a")
|#


#|
Bindings are for the numeric pad:
       thumb for  1a  (button _0_),
  forefinger for  1t  (button 1),
       pinky for  3   ("enter" button),
 when on rightside.

  7 8 9 -
  4 5 6 +
  1 2 3 enter   =>   1t    3
  _0_ . enter        _1a   3

 "0" is             "1a" activates primary button.
  double-wide.      "1t" toggles primary button.
 "enter" is         "3" is the secondary button.
  double-tall.



See also:
* Numeric pad layout for a buttonboard.
|#


 ;Press and release primary button (button 1).
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Insert")"pointer-button-activate.nu 1")

 ;Toggle primary button (button 1),
 ; f.e. either press or release for drag'n drop.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_End")"pointer-button-toggle.nu 1")

 ;Press and release secondary button (button 3).
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Enter")"pointer-button-activate.nu 3")