#|
Summary
Pointer placement by mathemagically halving the screen,
rather than arbitrary pointer motion.
Place and activate the pointer in any program, even with
remote client software and virtual machines,
without using a motionbox.
Pointer halving begins at the screen center.
Proceed left, right, up, or down and the pointer
indicates the narrowed area for placing the cursor.
Go back to the previous point, or return to
the screen center.
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
* Pointer composition 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 'pointer-halving.nu
* Background and intent
* Access
* Examples
* All functions that directly modify 'pointer-halving.nu
* defun pointer-halving-init.nu
Prepare the halving pointer.
* defun pointer-halving-points-init.nu
Initiate only splits and points for halving pointer.
* Commands for beginning pointer halving of current screen,
or revealing pointer placement.
* stumpwm:defcommand pointer-halving-begin.nu
Begin pointer halving at center of screen.
* stumpwm:defcommand pointer-halving-show.nu
Position, reshape, and reveal the halving pointer.
* stumpwm:defcommand pointer-halving-hide.nu
Hide the halving pointer.
* Commands for pointer halving the current screen.
* stumpwm:defcommand pointer-halving-left.nu
Put halving pointer left of halving point.
* stumpwm:defcommand pointer-halving-right.nu
Put halving pointer right of halving point.
* stumpwm:defcommand pointer-halving-up.nu
Put halving pointer up from halving point.
* stumpwm:defcommand pointer-halving-down.nu
Put halving pointer down from halving point.
* stumpwm:defcommand pointer-halving-back.nu
Put halving pointer at its prior point.
* Buttonboard bindings for pointer activation and placement
|#
#|#
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
Though the halving pointer does reposition
the Xserver cursor, the cursor can still be
used by itself, such as with a motionbox.
Hiding the pointer never hides the cursor.
The #'pointer-halving-show command will re-align
the cursor with the pointer again,
without needing to relocate (or hide)
the pointer itself.
Backtracking with the #'pointer-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 pointer with a horizontal toolbar.
Next, use left/right for selecting any option.
Then, no further need of up/down. Instead,
use the #'pointer-halving-back command to
backtrack some, and when needed use left/right again.
Bind pointer 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 pointer halving
commands to the numeric pad buttons can make use
of that area for pointer 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.
|#
#|#
Pointer composition and strategies
A single destination:
y axis
|
x axis --+--------------------------------
| origin up
| |
| | # destination
| |
| left --------+-------- right
| point |
| |
| |
| down
Extending lines from the point isolates the destination
within a quadrant and reveals the pointer trajectories
for intersecting with the destination.
Limiting the length of the lines to the width and height
of the narrowed region reveals the current precision
and the progress.
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 pointer 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.
The pointer has four parts that imply the crossing
of a horizontal line with a vertical line. A gap
for the implied intersection also provides the
visual clearance for the Xserver cursor.
Other programs continue controlling the appearance
of the cursor within their views without interference
from the halving pointer.
An Xwindow as narrow as a line and with a background color
is, for all intents and purposes, itself a line, and its
border with a contrasting color is the mask for the line.
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
"pointer-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 'pointer-halving.nu
|#
#|#
Background and intent
of root Xwindow property 'pointer-halving.nu
One supplemental pointer, its split count for each axis,
and its progress is retained as the 'pointer-halving.nu
property of the plist of the root Xwindow for a screen,
which is also the parent Xwindow for the pointer parts.
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 'pointer-halving.nu
Basic access of the plist of an Xwindow, f.e. 'Fxwin:
(getf (xlib:window-plist Fxwin) 'pointer-halving.nu)
Value for the plist property 'pointer-halving.nu is a list:
* Cons of two conses for the four pointer parts.
* Cons of number of splits for each axis, x then y.
* Series of alternating x and y for prior coordinates.
The pointer parts are a cons of two pairs of xlib:window.
Cons of:
Cons of horizontal parts: left and right.
Cons of vertical parts: top and bottom.
That is:
'((left-xwin . right-xwin)
. (top-xwin . bottom-xwin))
But, that is technically the same as (and becomes):
'((left-xwin . right-xwin)
top-xwin
. bottom-xwin)
Accessors for each part of the pointer,
f.e. after using #'car with #'getf for root-xwin:
(car
(getf (xlib:window-plist root-xwin) 'pointer-halving.nu))
left and right: car
left: car of car => caar
right: cdr of car => cdar
top and bottom: cdr
top: car of cdr => cadr
bottom: cdr of cdr => cddr
Accessors for the splits and coordinates,
f.e. after using #'cdr with #'getf for root-xwin:
(cdr
(getf (xlib:window-plist root-xwin) 'pointer-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 #'cadr with #'getf for root-xwin:
(cadr
(getf (xlib:window-plist root-xwin) 'pointer-halving.nu))
x-axis splits: car
y-axis splits: cdr
Accessors for only the coordinates (a list),
f.e. after using #'cddr with #'getf for root-xwin:
(cddr
(getf (xlib:window-plist root-xwin) 'pointer-halving.nu))
Most recent x: car
Most recent y: car of cdr => cadr
|#
#|#
Examples of root Xwindow property 'pointer-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:
'(((left-xwin . right-xwin)
top-xwin
. bottom-xwin)
; Begin split count.
(1 . 1)
; First point.
600 450)
For additional pointer 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 pointer is shifted
-225 along the y-axis:
'(((left-xwin . right-xwin)
top-xwin
. bottom-xwin)
; 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
(cdr
(getf
(xlib:window-plist root-xwin) 'pointer-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
(cddr
(getf
(xlib:window-plist root-xwin) 'pointer-halving.nu)))
(push x
(cddr
(getf
(xlib:window-plist root-xwin) 'pointer-halving.nu)))
|#
#|#
All functions that directly modify 'pointer-halving.nu
Initiating:
pointer-halving-init.nu
pointer-halving-points-init.nu
Commands:
pointer-halving-show.nu
pointer-halving-left.nu
pointer-halving-right.nu
pointer-halving-up.nu
pointer-halving-down.nu
pointer-halving-back.nu
|#
;#
(defun
pointer-halving-init.nu
(Froot)
"Prepare the halving pointer.
Froot is the root Xwindow of a screen.
Initiate the 'pointer-halving.nu property
of the Xwindow plist with a pointer,
begin its split count, and include the center
of the Xwindow as the first coordinates.
Return value:
'pointer-halving.nu property of the root Xwindow plist
See source document for background, intent, and examples."
(let*
( ;;;;Pointer for the root Xwindow, when existing.
(Lpointer-parts
(car(getf(xlib:window-plist Froot)'pointer-halving.nu)))
;;;;Function for making a single part for pointer.
(Lmake-pointer-part
(lambda
(&optional
(LAline-color"black")(LAcontrast-color"white"))
"Create an Xwindow for the halving pointer.
Optional LAline-color is the color for the background,
default as black.
Optional LAcontrast-color is the color for the border
and the midpoint, default as white.
The colors are for #'xlib:alloc-color, and can be
a name or the result from #'xlib:make-color."
(let
( ;;;;;;;;The Xwindow for the pointer part.
(LALpointer-part
(xlib:create-window :parent Froot
:x 0 :y 0
:border
(xlib:alloc-color
(xlib:window-colormap Froot)
LAcontrast-color)
:border-width 1
:background
(xlib:alloc-color
(xlib:window-colormap Froot)
LAline-color)
:width 2 :height 2)))
;;;;;;;Add middle point as a subwindow and draw it.
(xlib:map-window
(xlib:create-window :parent LALpointer-part
:x 0 :y 0
:background
(xlib:alloc-color
(xlib:window-colormap LALpointer-part)
LAcontrast-color)
:width 2 :height 2))
;;;;;;;Return pointer part.
LALpointer-part))))
;;;Remove prior pointer, when existing.
(when Lpointer-parts
(xlib:destroy-window(caar Lpointer-parts))
(xlib:destroy-window(cdar Lpointer-parts))
(xlib:destroy-window(cadr Lpointer-parts))
(xlib:destroy-window(cddr Lpointer-parts)))
;;;Add new pointer.
(setf
(getf(xlib:window-plist Froot)'pointer-halving.nu)
;;;;Create a list of generically defined Xwindows.
(list
(cons
;;;;;Pair of left and right parts.
(cons
(funcall Lmake-pointer-part)
(funcall Lmake-pointer-part))
;;;;;Pair of top and bottom parts.
(cons
(funcall Lmake-pointer-part"white""black")
(funcall Lmake-pointer-part"white""black"))))))
;;;Initiate splits and points.
(pointer-halving-points-init.nu Froot)
;;;Return the new value for immediate use.
(getf(xlib:window-plist Froot)'pointer-halving.nu))
;#
(defun
pointer-halving-points-init.nu
(Froot)
"Initiate only splits and points for pointer halving.
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 Xwindow center.
Replace any prior split count and coordinates.
Return value:
list of splits and coordinates,
t.i. only the cdr of 'pointer-halving.nu
of root Xwindow plist.
----
This initiates only the cdr of 'pointer-halving.nu,
t.i. the split counts and the points.
See #'pointer-halving-init.nu for initiating all
three parts, and the source document for background,
intent, and examples."
(setf
(cdr(getf(xlib:window-plist Froot)'pointer-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 for immediate use.
(cdr(getf(xlib:window-plist Froot)'pointer-halving.nu)))
#|
Commands for beginning pointer halving of current screen,
or revealing pointer placement.
|#
;#
(stumpwm:defcommand
pointer-halving-begin.nu
();no options
();no prompt
"Begin pointer halving at center of screen.
Also initiates pointer halving history."
(let
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
;;;Initiate split count and pointer coordinates,
;;; or everything when non-existing.
(if(cdr(getf(xlib:window-plist Lroot)'pointer-halving.nu))
(pointer-halving-points-init.nu Lroot)
(pointer-halving-init.nu Lroot)))
;;Place pointer at new coordinates.
(pointer-halving-show.nu))
;#
(stumpwm:defcommand
pointer-halving-show.nu
();no options
();no prompt
"Position, reshape, and reveal the halving pointer.
Position and shape the halving pointer for the screen,
and place the Xserver cursor with it.
Supplementary to #'pointer-halving-hide.nu function."
#|
Values for the calculations
(details in doc-pointer.txt)
The pointer gap is the only numerical value assigned.
The gap is presumed to be a square (width = height),
and is halved for centering the pointer parts.
The pointer line thickness (width) is presumed the same
for all pointer parts, and is halved for centering.
It is queried from only one pointer part, f.e. the height
of left part. Thus, it was set when the pointer parts
were initiated. See 'pointer-halving-init.nu.
It is for defining the breadth of a pointer part,
t.i. pointer line thickness + twice pointer border width.
Half of the pointer part breadth is needed for centering
a pointer part along the divides.
The midpoint is the (only) subwindow of a pointer part
and its width and height is presumed the same as
the pointer line thickness, as that is the point.
The minimal pointer line length is the same as the gap,
t.i. twice half of the gap.
The pointer mask (border) width is presumed the same
for all pointer parts. It is queried from only one
pointer part, f.e. the border width of the left part.
Thus, it was set when the pointer parts were initiated.
See 'pointer-halving-init.nu.
|#
(let*
#|
Precalculate essential values.
|#
( ;;;;The root Xwindow for current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen)))
;;;;The pointer parts for the screen, or initiate.
;;;; Left/right: caar, cdar.
;;;; Top/bottom: cadr, cddr.
(Lpointer
(car
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-halving-init.nu Lroot))))
;;;;Pointer gap is a square shape, evenly halvable.
;;;; Only half needed for centering.
;;;; This is the only value that is assigned because
;;;; 'pointer-halving-init.nu initiated the pointer parts.
(Lgap-half 8)
;;;;Minimal pointer line length is same as gap.
(Lline-length-min
(* Lgap-half 2))
;;;;Pointer line thickness (width).
;;;; Only half needed for centering.
;;;; Same for all parts; using the left part.
(Lthickness-half
(rash.nu(xlib:drawable-height(caar Lpointer))1))
;;;;Border for calculating full part length.
;;;; Same for all parts; using the left part.
(Lborder
(xlib:drawable-border-width(caar Lpointer)))
;;;;Pointer breadth is line thickness and borders.
;;;; Only half needed for centering any part.
(Lbreadth-half
(+ Lthickness-half Lborder))
;;;;Splits and points.
(Lsplits-xys
(cdr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;Length of horizontal split, along x-axis. Zero ok.
(Lsplit-x
(rash.nu(xlib:drawable-width Lroot)(caar Lsplits-xys)))
;;;;Length of vertical split, along y-axis. Zero ok.
(Lsplit-y
(rash.nu(xlib:drawable-height Lroot)(cdar Lsplits-xys)))
;;;;Current coordinates of pointer, its center.
(Lx(cadr Lsplits-xys))
(Ly(caddr Lsplits-xys)))
#|
Change the coordinates and size for all parts.
|#
;;;Left part of pointer.
(xlib:with-state((caar Lpointer))
(setf
;;;;Left width. Height stays the same.
(xlib:drawable-width(caar Lpointer))
(max Lline-length-min
(- Lsplit-x Lgap-half Lborder Lborder))
;;;;Left position, based on that width.
(xlib:drawable-x(caar Lpointer))
(- Lx Lgap-half
Lborder(xlib:drawable-width(caar Lpointer))Lborder)
(xlib:drawable-y(caar Lpointer))
(- Ly Lbreadth-half)))
;;;Left midpoint.
(setf
(xlib:drawable-x(car(xlib:query-tree(caar Lpointer))))
(-
(+(xlib:drawable-width(caar Lpointer))Lborder Lgap-half)
(rash.nu Lsplit-x 1)Lthickness-half))
;;;Right part of pointer.
(xlib:with-state((cdar Lpointer))
(setf
;;;;Right width. Height stays the same.
(xlib:drawable-width(cdar Lpointer))
(max Lline-length-min
(- Lsplit-x Lgap-half Lborder Lborder))
;;;;Right position.
(xlib:drawable-x(cdar Lpointer))
(+ Lx Lgap-half)
(xlib:drawable-y(cdar Lpointer))
(- Ly Lbreadth-half)))
;;;Right midpoint.
(setf
(xlib:drawable-x(car(xlib:query-tree(cdar Lpointer))))
(-(rash.nu Lsplit-x 1)
Lgap-half Lborder Lthickness-half))
;;;Top part of pointer.
(xlib:with-state((cadr Lpointer))
(setf
;;;;Top height. Width stays the same.
(xlib:drawable-height(cadr Lpointer))
(max Lline-length-min
(- Lsplit-y Lgap-half Lborder Lborder))
;;;;Top position, based on that height.
(xlib:drawable-x(cadr Lpointer))
(- Lx Lbreadth-half)
(xlib:drawable-y(cadr Lpointer))
(- Ly Lgap-half
Lborder(xlib:drawable-height(cadr Lpointer))Lborder)))
;;;Top midpoint.
(setf
(xlib:drawable-y(car(xlib:query-tree(cadr Lpointer))))
(-
(+(xlib:drawable-height(cadr Lpointer))Lborder Lgap-half)
(rash.nu Lsplit-y 1)Lthickness-half))
;;;Bottom part of pointer.
(xlib:with-state((cddr Lpointer))
(setf
;;;;Bottom height. Width stays the same.
(xlib:drawable-height(cddr Lpointer))
(max Lline-length-min
(- Lsplit-y Lgap-half Lborder Lborder))
;;;;Bottom position.
(xlib:drawable-x(cddr Lpointer))
(- Lx Lbreadth-half)
(xlib:drawable-y(cddr Lpointer))
(+ Ly Lgap-half)))
;;;Bottom midpoint.
(setf
(xlib:drawable-y(car(xlib:query-tree(cddr Lpointer))))
(-(rash.nu Lsplit-y 1)
Lgap-half Lborder Lthickness-half))
;;;Place Xserver cursor at the current halving point.
(xlib:warp-pointer Lroot Lx Ly)
;;;Ensure all pointer parts have priority.
;;; The Xserver cursor automatically has highest priority.
(setf
(xlib:window-priority(caar Lpointer)):above
(xlib:window-priority(cdar Lpointer)):above
(xlib:window-priority(cadr Lpointer)):above
(xlib:window-priority(cddr Lpointer)):above)
;;;Ensure all pointer parts are unhidden.
(xlib:map-window(caar Lpointer))
(xlib:map-window(cdar Lpointer))
(xlib:map-window(cadr Lpointer))
(xlib:map-window(cddr Lpointer))))
;#
(stumpwm:defcommand
pointer-halving-hide.nu
();no options
();no prompt
"Hide the halving pointer.
Nothing done to the Xserver cursor.
Supplementary to #'pointer-halving-show.nu function."
;;Make all pointer parts no longer viewable.
(let
( ;;;;The Xwindows for the pointer.
(Lpointer-parts
(car
(getf
(xlib:window-plist
(stumpwm:screen-root(stumpwm:current-screen)))
'pointer-halving.nu))))
(and Lpointer-parts
(xlib:unmap-window(caar Lpointer-parts))
(xlib:unmap-window(cdar Lpointer-parts))
(xlib:unmap-window(cadr Lpointer-parts))
(xlib:unmap-window(cddr Lpointer-parts)))))
#|
Commands for pointer halving the current screen.
|#
;#
(stumpwm:defcommand
pointer-halving-left.nu
();no options
();no prompt
"Put halving pointer left of halving point.
Shift halving pointer 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
(cddr
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-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
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu))
(cons LLx(cons LLy LLxys))))
(decf
(car
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
(max 1
(rash.nu(xlib:drawable-width Lroot)
;;;;;;;Also increment the x-axis split count.
(incf
(caadr
(getf(xlib:window-plist Lroot)'pointer-halving.nu))))))))
;;Place and reveal pointer for screen.
(pointer-halving-show.nu))
;#
(stumpwm:defcommand
pointer-halving-right.nu
();no options
();no prompt
"Put halving pointer right of halving point.
Shift halving pointer 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
(cddr
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-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
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu))
(cons LLx(cons LLy LLxys))))
(incf
(car
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
(max 1
(rash.nu(xlib:drawable-width Lroot)
;;;;;;;Also increment the x-axis split count.
(incf
(caadr
(getf(xlib:window-plist Lroot)'pointer-halving.nu))))))))
;;Place and reveal pointer for screen.
(pointer-halving-show.nu))
;#
(stumpwm:defcommand
pointer-halving-up.nu
();no options
();no prompt
"Put halving pointer up from halving point.
Shift halving pointer 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
(cddr
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-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
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu))
(cons LLx(cons LLy LLxys))))
(decf
(cadr
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
(max 1
(rash.nu(xlib:drawable-height Lroot)
;;;;;;;Also increment the y-axis split count.
(incf
(cdadr
(getf(xlib:window-plist Lroot)'pointer-halving.nu))))))))
;;Place and reveal pointer for screen.
(pointer-halving-show.nu))
;#
(stumpwm:defcommand
pointer-halving-down.nu
();no options
();no prompt
"Put halving pointer down from halving point.
Shift halving pointer 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 of the 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
(cddr
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-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
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;;;The first pair of coordinates, x and y.
(LLx(car LLxys))(LLy(cadr LLxys)))
(setf
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu))
(cons LLx(cons LLy LLxys))))
(incf
(cadr
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
(max 1
(rash.nu(xlib:drawable-height Lroot)
;;;;;;;Also increment the y-axis split count.
(incf
(cdadr
(getf(xlib:window-plist Lroot)'pointer-halving.nu))))))))
;;Place and reveal pointer for screen.
(pointer-halving-show.nu))
;#
(stumpwm:defcommand
pointer-halving-back.nu
();no options
();no prompt
"Put halving pointer at its prior point."
#|
When more than one pair of coordinates,
remove first pair because it is current position,
then use the next pair for pointer placement.
|#
(let
( ;;;;The root Xwindow of the current screen.
(Lroot
(stumpwm:screen-root(stumpwm:current-screen))))
;;;Attempted interaction with the pointer means
;;; its existence is desired, so ensure it is.
(or(getf(xlib:window-plist Lroot)'pointer-halving.nu)
(pointer-halving-init.nu Lroot))
;;;Must be at least two pairs, t.i. four coordinates.
(and
(< 3
(length
(cddr(getf(xlib:window-plist Lroot)'pointer-halving.nu))))
(let*
( ;;;;;;Just the split counts and coordinates.
(LLsplits-xys
(cdr(getf(xlib:window-plist Lroot)'pointer-halving.nu)))
;;;;;;Remove first pair of coordinates; keep for comparing.
(LLx(pop(cdr LLsplits-xys)))
(LLy(pop(cdr LLsplits-xys))))
#|?
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(cdr LLsplits-xys)))
(decf(car(car LLsplits-xys))))
;;;;;Decrease split count for y-axis when prior x was same.
(and
(= LLx(car(cdr LLsplits-xys)))
(decf(cdr(car LLsplits-xys)))))))
;;Place and reveal pointer for screen.
(pointer-halving-show.nu))
#|#
Buttonboard bindings for pointer activation and placement.
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 H
4 5 6 + => L D R S
1 2 3 enter b B 3
_0_ . enter _1a_ 3
"0" is "B" is "pointer-halving-begin".
double-wide. "b" is "pointer-halving-back".
"enter" is "1a" activates primary button.
double-tall. "1t" toggles primary button.
"clear" is really "3" is the secondary button.
Num_Lock.
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 H
4 5 6 + => L D R S
1 2 3 enter b B
_0_ . enter
Or, consider the arrow buttons in between:
up 1 2 3 enter
Control left down right _0_ . enter
||
\||/
\/
3
U b B S H
Hyper L D R _1a_ 1t H
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")"pointer-halving-left.nu")
;Right
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Right")"pointer-halving-right.nu")
;Up
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Up")"pointer-halving-up.nu")
;Down
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Begin")"pointer-halving-down.nu")
;Begin, back, reveal current point for current screen.
;Begin pointer halving at screen center.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Down")"pointer-halving-begin.nu")
;Back to prior halving point.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_End")"pointer-halving-back.nu")
;Show the pointer.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Add")"pointer-halving-show.nu")
;Hide the pointer.
(stumpwm:define-key stumpwm:*top-map*
(stumpwm:kbd"KP_Subtract")"pointer-halving-hide.nu")