#|
Summary


Xserver cursor placement by mathemagically halving
 the screen, rather than arbitrary pointer motion.
Place and activate the Xserver cursor in any program,
 even with remote client software and virtual machines,
 without using a motionbox.

Cursor halving begins at the screen center.
 Proceed left, right, up, or down.  Go back to
 the previous point, or return to the screen center.

Recenter at any halving point and it becomes
 the beginning point.  Going back stops at
 that halving point.
Returning to the beginning returns to that recenter point,
 then returning once more makes the screen center as
 the beginning point once again.

Buttonboard bindings for sending pointer events
 to the Xserver.  (Independent from symbols of this doc.)
Activate (both press and release) either the primary
 or the secondary button for menus, toolbars, and so forth.
Toggle primary button (press or release) for drag'n drop,
 text selection, and so forth.
|#


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


#|
Content outline


* Observations and interests

* Requirements

* Considerations

* Xserver cursor position and strategies

* Opportunities, when with additional programming

* General function use and naming conventions


* Ancillary function.
  (Independent from symbols of this document.)

 * defun rash.nu
    Arithmetic shift positively to the right.
    Reverse direction of #'ash bitshifting.


* Complementary commands.
  (Independent from symbols of this document.)

 * 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.


* CLX root Xwindow plist property 'cursor-halving.nu

 * Background and intent

 * Access

 * Examples

 * All functions that directly modify 'cursor-halving.nu

 * defun cursor-halving-init.nu
    Prepare for cursor halving the screen.


* Commands for beginning cursor halving the current screen,
  or returning the Xserver cursor to the halving point.

 * stumpwm:defcommand cursor-halving-begin.nu
    Begin cursor halving at screen center or the recenter.

 * stumpwm:defcommand cursor-halving-recenter.nu
    Make current halving point the beginning for halving.

 * stumpwm:defcommand cursor-halving-current.nu
    Put Xserver cursor at the current halving point.


* Commands for cursor halving the current screen.

 * stumpwm:defcommand cursor-halving-left.nu
    Put Xserver cursor left of halving point.

 * stumpwm:defcommand cursor-halving-right.nu
    Put Xserver cursor right of halving point.

 * stumpwm:defcommand cursor-halving-up.nu
    Put Xserver cursor up from halving point.

 * stumpwm:defcommand cursor-halving-down.nu
    Put Xserver cursor down from halving point.

 * stumpwm:defcommand cursor-halving-back.nu
    Put Xserver cursor at prior halving point.


* Buttonboard bindings for pointer activation
   and cursor halving
|#


#|#
Observations and interests


The left-top corner is a common origin in each view,
 related to writing from left-to-right/top-to-bottom.
Menus or toolbars are typically located from that
 common origin, making the same coordinates relevant
 amongst different computer programs.

Various regions of a view offer their options without
 ever changing them, such as the menus or toolbars.
 Their subregions are allocated to commands that
 often require activation by a pointer.

Knowing coordinates anywhere within a subregion
 is enough for activating that command by means
 of sending a pointer event to the Xserver.
Simply place the pointer at the coordinates
 and then send the button-press event
 followed by the button-release event.
 No need for a motionbox.

Screen size can be changed, though root Xwindow size seems
 unchangable.  Contents of root Xwindow still visible when
 screen size is smaller.  Depend on root Xwindow size
 instead of screen size.
|#


#|#
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
|#


#|#
Considerations


Backtracking with the #'cursor-halving-back command
 can be convenient with half a screen, or a quadrant,
 or similarly split regions, rather than starting over
 from the screen center.

Using one pair of directions at a time
 makes backtracking very effective
 for multiple destinations aligned
 either horizontally or vertically.
For example, use up/down to vertically align
 the Xserver cursor with a horizontal toolbar.
 Next, use left/right for selecting any option.
Then, no further need of up/down.  Instead,
 use the #'cursor-halving-back command to
 backtrack some, and when needed use left/right again.

Bind cursor commands to buttons distinct from editing,
 f.e. the numeric pad buttons of an extended buttonboard.
Or, bind commands to the numbers above the letters,
 and depend on the numeric pad for numbers instead.
Or, perhaps the F buttons, often above the numbers.
Either way, including modifier buttons is optional,

A buttonboard extended with a numeric button pad
 also extends into the desk space often used
 for a motionbox.  Binding the cursor halving
 commands to the numeric pad buttons can make use
 of that area for cursor placement, as well as
 free the desk space from the motionbox.
See the button bindings at the end of this document.

Retain the use of the numeric pad for its original intent
 by associating a modifier button with the commands.
For example, the Hyper modifier is typically unused,
 so the "H-KP_Enter" button binding for a command
 works without interfering with the "KP_Enter" button
 for its usually action.

The hyper modifier button is often unique
 because it is hardly ever on a buttonboard,
 so unlikely to conflict with pre-defined
 bindings of other programs.
Use xmodmap to re-assign a button to Hyper,
 like the caps lock button, or spare one
 of the other modifier buttons.

Disconnecting any unused motionboxes
 prevents accidental movement of the
 Xserver cursor.
|#


#|#
Xserver cursor position and strategies

A single destination:

          y axis
          |
 x axis --+--------------------------------
          | origin       up
          |              |
          |              |    # destination
          |              |
          | left --------+-------- right
          |        point |
          |              |
          |              |
          |             down


Many potential destinations:

          y axis
          |
 x axis --+---------------------------------------
          | origin           up
          |                  |
          |                  |
          |                  | destinations
          | left ------------+--#--#--#--#-- right
          |            point |
          |                  |
          |                  |
          |                 down

Multiple potential destinations might be axially aligned,
 f.e. a menu of options or a toolbar of outlined regions.
Placing the Xserver cursor first along their axial alignment
 frees the other axis for selecting any of the items.
 Another option can be chosen by backtracking a little
 rather than starting over at the center of the screen.


See also:
* Observations about pointers, coordinates, and halving.
|#


#|#
Opportunities, when with additional programming

Any option available by means of only pointer activation
 also becomes available as a typable command,
 or for custom menus external to a program,
 or for including in a custom sequence of commands.

It is an alternative for binding commands to buttons
 on a buttonboard, especially for lack of options
 within either a program or an operating system
 (even with remote software, f.e. VNC or RDP).
In other words, a personlized set of bindings
 that works without having to re-assign them
 for each and every operating system or program.
|#


#|
Hereafter, there be code.
|#

#|#
General function use and naming conventions


"cursor-halving": Collective prefix 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.
|#


#|
Ancillary function.
(Independent from symbols of this document.)
|#


 ;#
(defun
rash.nu
(Finteger Fcount)
"Arithmetic shift of Finteger by Fcount, positively right.

Reverse the direction of bitshifting for function #'ash.
 Negative value of Fcount is left (t.i. multiply by 2),
 positive value of Fcount is right (t.i. divide by 2)."
(ash Finteger(- 0 Fcount)))


#|
Complementary commands.

From the independent pointer button activation 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)))))


#|
CLX root Xwindow plist property 'cursor-halving.nu
|#


#|#
Background and intent
 of root Xwindow property 'cursor-halving.nu


The split count for each axis and the halving progress
 is retained as the 'cursor-halving.nu plist property of
 the root Xwindow of a screen.
An Xwindow has a property list available with
 the CLX accessor "xlib:window-plist".

The split count for halving an axis is the same as the count
 for bitshifting the axis length, effectively dividing it by
 a magnitude of 2 (with truncation) and providing the length
 of the next halving.

A series of alternating x and y coordinates retains prior
 placements for the convenience of reversing the progress,
 beginning with the most recent point as its car and cadr.
|#


#|#
Access of root Xwindow property 'cursor-halving.nu


Basic access of the plist of an Xwindow, f.e. 'Fxwin:
 (getf (xlib:window-plist Fxwin) 'cursor-halving.nu)

Value for the plist property 'cursor-halving.nu is a list:
 * Cons of number of splits for each axis, x then y.
 * Series of alternating x and y for prior coordinates.


Accessors for the splits and coordinates,
 f.e. after using #'getf for root-xwin:
  (getf (xlib:window-plist root-xwin) 'cursor-halving.nu)

  Split count as a cons (x . y):  car
   x-axis splits:  car of car  => caar
   y-axis splits:  cdr of car  => cdar

  Recent point and the remnants:   cdr
   most recent x:  car  of cdr  => cadr
   most recent y:  cadr of cdr  => caddr


Accessors for only the splits (a cons),
 f.e. after using #'car with #'getf for root-xwin:
  (car
   (getf (xlib:window-plist root-xwin) 'cursor-halving.nu))

   x-axis splits:  car
   y-axis splits:  cdr


Accessors for only the coordinates (a list),
 f.e. after using #'cdr with #'getf for root-xwin:
  (cdr
   (getf (xlib:window-plist root-xwin) 'cursor-halving.nu))

  Most recent x:  car
  Most recent y:  car of cdr  => cadr
|#


#|#
Examples of root Xwindow property 'cursor-halving.nu


Initiating at the screen center is one split
 for each axis.  The list continues with
 the unpaired coordinates.

For example, initialization for a screen
 of 1200 by 900:

  '(
    ; Begin split count.
    (1 . 1)
    ; First point.
    600 450)


For additional cursor placements, increment
 the split count for the traversed axis,
 and prepend the resulting x and y coordinates
 to the list of coordinates.

For example, when the next split is upwards for
 the prior example, then the cursor is shifted
 -225 along the y-axis:

  '(
    ; Split count.
    (1 . 2)
    ; Most recent point, then remnant.
    600 225 600 450)


Splits are increased or decreased by one,
 such as with the default values for functions
 #'incf and #'decf.

For example, increase split count for y-axis of root-xwin:
  (incf
   (cdar
    (getf
     (xlib:window-plist root-xwin) 'cursor-halving.nu)))


Consider #'push for adding new coordinates,
 first for the y-axis and then for the x-axis.

For example, add new coordinates x and y for root-xwin:
  (push y
   (cdr
    (getf
     (xlib:window-plist root-xwin) 'cursor-halving.nu)))
  (push x
   (cdr
    (getf
     (xlib:window-plist root-xwin) 'cursor-halving.nu)))
|#


#|#
All functions that directly modify 'cursor-halving.nu


Initiating:
 cursor-halving-init.nu

Commands:
 cursor-halving-recenter.nu
 cursor-halving-left.nu
 cursor-halving-right.nu
 cursor-halving-up.nu
 cursor-halving-down.nu
 cursor-halving-back.nu
|#


 ;#
(defun
cursor-halving-init.nu
(Froot)
"Prepare for cursor halving the screen.

Froot is the root Xwindow of a screen.

Initiate the split count for the axes, and the series
 of coordinates with the point of the screen center.
 Replace any prior split count and coordinates.

Return value:
 'cursor-halving.nu property of the root Xwindow plist

See source document for background, intent, and examples."
(setf
(getf(xlib:window-plist Froot)'cursor-halving.nu)
(list
 ;;;;Initiate split count for both axes.
#|?
Using '(1 . 1) somehow fails to get acknowledged
 when replacing split count data.  Prior cons remains.
Using (cons 1 1) seems to reliably assert the
 new value for the car of the data.
?|#
(cons 1 1)
 ;;;;Initiate points with root Xwindow center.
 ;;;; Bitshifting for consistent division by 2 (truncated).
(rash.nu(xlib:drawable-width Froot)1)
(rash.nu(xlib:drawable-height Froot)1)))
 ;;Return the new value for immediate use.
(getf(xlib:window-plist Froot)'cursor-halving.nu))


#|
Commands for beginning cursor halving the current screen,
 or returning the Xserver cursor to the halving point.
|#


 ;#
(stumpwm:defcommand
cursor-halving-begin.nu
();no options
();no prompt
"Begin cursor halving the screen from its center.

Cursor halving begins at the screen center (root Xwindow).
 After cursor halving left, right, up, or down,
 use #'cursor-halving-recenter for making
 the current halving point the beginning point.

Make the screen center the beginning point again when
 the halving point is already the recenter point.

Remove cursor halving history each time."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;;Initiate split count and halving coordinates when none.
(if(not(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cursor-halving-init.nu Lroot)
 ;;;;Otherwise, restore beginning point when more than one.
(if
 ;;;;;Two coordinates per point means 4 for two points.
(> 4
(length
(cddr(getf(xlib:window-plist Lroot)'cursor-halving.nu))))
 ;;;;;Only one point.  Make screen center the beginning.
(cursor-halving-init.nu Lroot)
 ;;;;;Otherwise, remove all points except the last one.
 ;;;;; #'do is really "do until condition becomes true".
(do
();no variables
( ;;;;;;;Do until length is less then 3.
(> 3
(length
(cddr
(getf(xlib:window-plist Lroot)'cursor-halving.nu)))))
 ;;;;;;Simply go back until at beginning; the splits, too.
(cursor-halving-back.nu)))))
 ;;Place cursor at new coordinates.
(cursor-halving-current.nu))


 ;#
(stumpwm:defcommand
cursor-halving-recenter.nu
();no options
();no prompt
"Make current halving point the start for halving."
#|
Use most recent pair of coordinates of halving point
 for beginning cursor halving.  As the new beginning,
 there is no need for the prior coordinates.  However,
 the splits are still relevant.
No need for initiating when nonexistent because
 there would be nothing to do anyway.
|#
 ;;No need when only one point, or when nonexistent.
(and
(< 3
(length
(cddr
(getf
(xlib:window-plist
(stumpwm:screen-root(stumpwm:current-screen)))
'cursor-halving.nu))))
(let*
( ;;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen)))
(Lxys
(cddr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
(Lx(car Lxys))(Ly(cadr Lxys)))
 ;;;;Replace remaining series with the first pair.
(setf
(cddr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(list Lx Ly)))))


 ;#
(stumpwm:defcommand
cursor-halving-current.nu
();no options
();no prompt
"Put Xserver cursor at the current halving point."
(let*
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen)))
 ;;;;Coordinates only.  Initiate when needed.
(Lxys
(or
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cdr(cursor-halving-init.nu Lroot)))))
 ;;;Place Xserver cursor at the current halving point.
 ;;; The Xserver cursor is actually for an Xwindow,
 ;;; so using the root Xwindow of the screen.
(xlib:warp-pointer Lroot(car Lxys)(cadr Lxys))))



#|
Commands for cursor halving the current screen.
|#


 ;#
(stumpwm:defcommand
cursor-halving-left.nu
();no options
();no prompt
"Put Xserver cursor left of halving point.

Shift Xserver cursor leftward from vertex of last x-axis
 split for screen (else immediately from center)
 at least one pixel within the screen bounds.  Reveal."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;;Decrease the x coordinate (by at least 1)
 ;;; when greater than 0.  Use same y coordinate.
(when
(< 0
(car
(cdr
(or(getf(xlib:window-plist Lroot)'cursor-halving.nu)
(cursor-halving-init.nu Lroot)))))
 ;;;;Simply prepend copies of most recent coordinates, then
 ;;;; subtract the split length from the copied x coordinate.
(let*
( ;;;;;;All of the coordinates.
(LLxys
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
 ;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cons LLx(cons LLy LLxys))))
(decf
(car(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
(max 1
(rash.nu(xlib:drawable-width Lroot)
 ;;;;;;;Also increment the x-axis split count.
(incf
(caar(getf(xlib:window-plist Lroot)'cursor-halving.nu))))))))
 ;;Place the Xserver cursor.
(cursor-halving-current.nu))


 ;#
(stumpwm:defcommand
cursor-halving-right.nu
();no options
();no prompt
"Put Xserver cursor right of halving point.

Shift Xserver cursor rightward from vertex of last x-axis
 split for screen (else immediately from its center)
 at least one pixel within the screen bounds.  Reveal."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;;Increase the x coordinate (by at least 1)
 ;;; when less than screen width.  Use same y coordinate.
(when
(>(xlib:drawable-width Lroot)
(car
(cdr
(or(getf(xlib:window-plist Lroot)'cursor-halving.nu)
(cursor-halving-init.nu Lroot)))))
 ;;;;Simply prepend copies of most recent coordinates, then
 ;;;; add the split length to the copied x coordinate.
(let*
( ;;;;;;All of the coordinates.
(LLxys
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
 ;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cons LLx(cons LLy LLxys))))
(incf
(car(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
(max 1
(rash.nu(xlib:drawable-width Lroot)
 ;;;;;;;Also increment the x-axis split count.
(incf
(caar(getf(xlib:window-plist Lroot)'cursor-halving.nu))))))))
 ;;Place the Xserver cursor.
(cursor-halving-current.nu))


 ;#
(stumpwm:defcommand
cursor-halving-up.nu
();no options
();no prompt
"Put Xserver cursor up from halving point.

Shift Xserver cursor upward from vertex of last y-axis
 split for screen (else immediately from its center)
 at least one pixel within the screen bounds.  Reveal."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;;Decrease the y coordinate (by at least 1)
 ;;; when greater than 0.  Use same x coordinate.
(when
(< 0
(cadr
(cdr
(or(getf(xlib:window-plist Lroot)'cursor-halving.nu)
(cursor-halving-init.nu Lroot)))))
 ;;;;Simply prepend copies of most recent coordinates, then
 ;;;; subtract the split length from the copied y coordinate.
(let*
( ;;;;;;All of the coordinates.
(LLxys
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
 ;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cons LLx(cons LLy LLxys))))
(decf
(cadr
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
(max 1
(rash.nu(xlib:drawable-height Lroot)
 ;;;;;;;Also increment the y-axis split count.
(incf
(cdar
(getf(xlib:window-plist Lroot)'cursor-halving.nu))))))))
 ;;Place the Xserver cursor.
(cursor-halving-current.nu))


 ;#
(stumpwm:defcommand
cursor-halving-down.nu
();no options
();no prompt
"Put Xserver cursor down from halving point.

Shift Xserver cursor downward from vertex of last y-axis
 split for screen (else immediately from its center)
 at least one pixel within the screen bounds.  Reveal."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;;Decrease the y coordinate (by at least 1)
 ;;; when less than screen height.  Use same x coordinate.
(when
(>(xlib:drawable-height Lroot)
(cadr
(cdr
(or(getf(xlib:window-plist Lroot)'cursor-halving.nu)
(cursor-halving-init.nu Lroot)))))
 ;;;;Simply prepend copies of most recent coordinates, then
 ;;;; add the split length to the copied y coordinate.
(let*
( ;;;;;;All of the coordinates.
(LLxys
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
 ;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))
(cons LLx(cons LLy LLxys))))
(incf
(cadr
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
(max 1
(rash.nu(xlib:drawable-height Lroot)
 ;;;;;;;Also increment the y-axis split count.
(incf
(cdar
(getf(xlib:window-plist Lroot)'cursor-halving.nu))))))))
 ;;Place the Xserver cursor.
(cursor-halving-current.nu))


 ;#
(stumpwm:defcommand
cursor-halving-back.nu
();no options
();no prompt
"Put Xserver cursor at prior halving point."
#|
When more than one pair of coordinates,
 remove first pair because it is current position,
 then use the next pair for cursor placement.
|#
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
 ;;Attempted backtracking indicates an interest
 ;; in cursor halving, so ensure it was inititated.
(or(getf(xlib:window-plist Lroot)'cursor-halving.nu)
(cursor-halving-init.nu Lroot))
 ;;;Must be at least two pairs, t.i. four coordinates.
(and
(< 3
(length
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))))
(let*
( ;;;;;;Just the split counts and coordinates.
(LLsplits
(car(getf(xlib:window-plist Lroot)'cursor-halving.nu)))
 ;;;;;;Remove first pair of coordinates; keep for comparing.
(LLx
(pop
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))))
(LLy
(pop
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))))
 ;;;;;;The remaining coordinates.
(LLxys
(cdr(getf(xlib:window-plist Lroot)'cursor-halving.nu))))
#|?
Failed when trying to use a conditional to provide
 the place for decf: (decf(or(and...)(and...)))
 Error referred to ":GO", t.i. (:GO(and...))
Perhaps "or" or "and" failed to return a place?
?|#
 ;;;;;Decrease split count for x-axis when prior y was same.
(and(= LLy(cadr LLxys))
(decf(car LLsplits)))
 ;;;;;Decrease split count for y-axis when prior x was same.
(and(= LLx(car LLxys))
(decf(cdr LLsplits))))))
 ;;Place the Xserver cursor.
(cursor-halving-current.nu))


#|#
Buttonboard bindings for Xserver cursor placement
 and pointer activation.


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:

  clear
  7 8 9 -                1t U
  4 5 6 +         =>     L  D R C
  1 2 3 enter            b  B r 3
  _0_ . enter            _1a_   3

 "0" is                 "B" is "cursor-halving-begin".
  double-wide.          "b" is "cursor-halving-back".
 "enter" is             "r" is "cursor-halving-recenter".
  double-tall.          "1a" activates primary button.
 "clear" is really      "1t" toggles primary button.
  Num_Lock.             "3" is the secondary button.

 Thumb (0, was "KP_Insert") for button 1a,
 and pinky (enter, "KP_Enter") for button 3,
 when on rightside.


But maybe consider instead:

  clear              1t
  7 8 9 -            1a U 3
  4 5 6 +       =>   L  D R C
  1 2 3 enter        b  B r
  _0_ . enter


Or, consider the arrow buttons in between:

                   up           1 2 3 enter
  Control   left  down  right   _0_ . enter

                   ||
                  \||/
                   \/
                                3  r
                   U            b  B  C
   Hyper     L     D     R      _1a_  1t

 Thumb for Hyper modifier held with arrow buttons
 for halving in those directions, and pinky for button 1a,
 button 3, and the rest.  Probably no need for modifier
 with numeric pad, unless numeric pad is ever used.

Consider xmodmap for remapping the Control button
 to Hyper when Control and arrow buttons are used
 in programs, like in some text editors.

For example, for Hyper-up:
   (stumpwm:kbd "H-Up")
 or for Control-up
   (stumpwm:kbd "C-Up")


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


 ;Activate pointer.

 ;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), for drag'n drop.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Home")"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")


 ;Direction of halving the current screen.

 ;Left
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Left")"cursor-halving-left.nu")

 ;Right
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Right")"cursor-halving-right.nu")

 ;Up
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Up")"cursor-halving-up.nu")

 ;Down
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Begin")"cursor-halving-down.nu")


 ;Begin, back, or show current point for current screen.

 ;Begin cursor halving.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Down")"cursor-halving-begin.nu")

 ;Back to prior halving point.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_End")"cursor-halving-back.nu")

 ;Make current halving point the beginning point.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Page_Down")"cursor-halving-recenter.nu")

 ;Place Xserver cursor at current halving point.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Add")"cursor-halving-current.nu")