; ------------------------------------------------------------------------
; HeavyThing x86_64 assembly language library and showcase programs
; Copyright © 2015-2018 2 Ton Digital
; Homepage: https://2ton.com.au/
; Author: Jeff Marrison <jeff@2ton.com.au>
;
; This file is part of the HeavyThing library.
;
; HeavyThing is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License, or
; (at your option) any later version.
;
; HeavyThing is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License along
; with the HeavyThing library. If not, see <http://www.gnu.org/licenses/>.
; ------------------------------------------------------------------------
;
; tui_object.inc: the base object for all things TUI.
;
; every kind of object that needs to display/draw things needs a base
; set of functionality like draw buffers, positioning, size, children
; events, etc.
;
; this is done in a "pseudo OOP" way, similar to how I did up the
; epoll vmethod tables.
;
; I suppose that means it isn't really pseudo, insofar as I am still
; doing inheritance and so on, hahah...
;
include 'tui_geometry.inc'
; due to our reliance on epoll$timer goods, our vmethod table
; must be similar (at least for the first few methods)
; to epoll's such that we can "fake" being an epoll object even
; when we are not one
; this is an "interesting" translation from my perfectly working C++
; versions, haha... maybe I am 3/4th the way to being properly
; crazy, HA! (that being said, this assembler version is far and away better)
; some notes here on layout goods:
; if layout == vertical, then we pay attention to horizontal alignment (left, center, right)
; if layout == horizontal, then we pay attention to vertical alignment (top, middle, bottom)
; further notes/caution/advice on percentage based width/height calculations:
; there are many ways to introduce rounding-based errors into the mix. we take care of the case
; where rounding results in one too many, but for the other way, where we end up rounding away
; one, care/screwing around must be taken to make sure that resizing through all of your
; possibilities yields the right result. the tui_simpleauth is a good example of this, where
; the top third of screenspace i declare as 50% high, then a fixed height midsection, and then
; the bottom third as another 51%, which admittedly seems weird on its face, but this eliminates
; the rounding issue for available space left (all depends on how you are slicing them up as to
; whether this really matters or not)
;
if used tui_object$vtable | used tui_object$default_vtable | defined include_everything
; so our base tui_object default vtable is:
dalign
tui_object$default_vtable:
dq tui_object$cleanup, tui_object$clone, tui_object$draw, tui_object$redraw, tui_object$updatedisplaylist, tui_object$sizechanged
dq tui_object$timer, tui_object$layoutchanged, tui_object$move, tui_object$setfocus, tui_object$gotfocus, tui_object$lostfocus
dq tui_object$keyevent, tui_object$domodal, tui_object$endmodal, tui_object$exit, tui_object$calcbounds, tui_object$calcchildbounds
dq tui_object$appendchild, tui_object$appendbastard, tui_object$prependchild, tui_object$contains, tui_object$getchildindex
dq tui_object$removechild, tui_object$removebastard, tui_object$removeallchildren, tui_object$removeallbastards
dq tui_object$getobjectsunderpoint, tui_object$flatten, tui_object$firekeyevent, tui_object$ontab, tui_object$onshifttab
dq tui_object$setcursor, tui_object$showcursor, tui_object$hidecursor, tui_object$click, tui_object$clicked
end if
if used tui_object$simple_vtable | defined include_everything
; this one can be used directly, but only for nonfunctional tui_object direct descendents (see tui_spacers.inc for examples on why this is necessary)
dalign
tui_object$simple_vtable:
dq tui_object$cleanup, tui_object$simple_clone, tui_object$draw, tui_object$redraw, tui_object$updatedisplaylist, tui_object$sizechanged
dq tui_object$timer, tui_object$layoutchanged, tui_object$move, tui_object$setfocus, tui_object$gotfocus, tui_object$lostfocus
dq tui_object$keyevent, tui_object$domodal, tui_object$endmodal, tui_object$exit, tui_object$calcbounds, tui_object$calcchildbounds
dq tui_object$appendchild, tui_object$appendbastard, tui_object$prependchild, tui_object$contains, tui_object$getchildindex
dq tui_object$removechild, tui_object$removebastard, tui_object$removeallchildren, tui_object$removeallbastards
dq tui_object$getobjectsunderpoint, tui_object$flatten, tui_object$firekeyevent, tui_object$ontab, tui_object$onshifttab
dq tui_object$setcursor, tui_object$showcursor, tui_object$hidecursor, tui_object$click, tui_object$clicked
end if
; descendents can swap in their own virtual methods just like the epoll layer.
; every tui_object contains the following variables:
tui_vtable_ofs = 0 ; pointer to a vtable
tui_bounds_ofs = 8 ; rect itself, not a pointer
tui_bounds_ax_ofs = 8
tui_bounds_ay_ofs = 12
tui_bounds_bx_ofs = 16
tui_bounds_by_ofs = 20
tui_width_ofs = 24 ; dd
tui_widthperc_ofs = 28 ; dq (double)
tui_height_ofs = 36 ; dd
tui_heightperc_ofs = 40 ; dq (double)
tui_visible_ofs = 48 ; dd
tui_includeinlayout_ofs = 52 ; dd
tui_absolutex_ofs = 56 ; dd
tui_absolutey_ofs = 60 ; dd
tui_text_ofs = 64 ; dq (pointer to buffer)
tui_attr_ofs = 72 ; dq (pointer to buffer)
tui_focus_ofs = 80 ; dq (pointer to a tui_object or descendent)
tui_parent_ofs = 88 ; dq (pointer to a tui_object or descendent)
tui_layout_ofs = 96 ; dd
tui_horizalign_ofs = 100 ; dd
tui_vertalign_ofs = 104 ; dd
tui_bastardglue_ofs = 108 ; dd
tui_displayname_ofs = 112 ; dq
tui_children_ofs = 120 ; dq (list$)
tui_bastards_ofs = 128 ; dq (list$)
tui_dropshadow_ofs = 136 ; dd
tui_scroll_ofs = 140 ; two dd's (a Point)
tui_object_size = 148
; our layout constants
tui_layout_vertical = 0
tui_layout_horizontal = 1
tui_layout_absolute = 2
; our horizontal alignment constants
tui_align_left = 0
tui_align_center = 1
tui_align_right = 2
; our vertical alignment constants
tui_align_top = 0
tui_align_middle = 1
tui_align_bottom = 2
; our virtual method table/function call offsets:
tui_vcleanup = 0
tui_vclone = 8
tui_vdraw = 16
tui_vredraw = 24
tui_vupdatedisplaylist = 32
tui_vsizechanged = 40
tui_vtimer = 48
tui_vlayoutchanged = 56
tui_vmove = 64
tui_vsetfocus = 72
tui_vgotfocus = 80
tui_vlostfocus = 88
tui_vkeyevent = 96
tui_vdomodal = 104
tui_vendmodal = 112
tui_vexit = 120
tui_vcalcbounds = 128
tui_vcalcchildbounds = 136
tui_vappendchild = 144
tui_vappendbastard = 152
tui_vprependchild = 160
tui_vcontains = 168
tui_vgetchildindex = 176
tui_vremovechild = 184
tui_vremovebastard = 192
tui_vremoveallchildren = 200
tui_vremoveallbastards = 208
tui_vgetobjectsunderpoint = 216
tui_vflatten = 224
tui_vfirekeyevent = 232
tui_vontab = 240
tui_vonshifttab = 248
tui_vsetcursor = 256
tui_vshowcursor = 264
tui_vhidecursor = 272
tui_vclick = 280
tui_vclicked = 288
; some notes here on constructing a tui_object:
; the base tui_object is not meant to be constructed by itself
; and the reason of course for that is simply because
; you would get zero functionality from the base layer (nothing to
; draw, nothing to do, etc)
; as a result, DESCENDENT objects get constructors, and then
; call the tui_object's appropriate initialization methods
; as such, there are not tui_object$new... functions, only
; tui_object$init functions
; ALL DESCENDENTS _MUST_ set their own vtable! (these init funcs don't do it for you)
; it can be set _before_ or after calling the init functions herein
; we do not overwrite/zero/clear the first 8 bytes of the object, thus leaving it
; wholly uninitialised for the descendents to fill
if used tui_object$init_defaults | defined include_everything
; single parameter in rdi: the tui_object
falign
tui_object$init_defaults:
prolog tui_object$init_defaults
push rdi
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
mov rdi, [rsp]
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
pop rdi
epilog
end if
if used tui_object$simple_clone | defined include_everything
; single argument in rdi: tui object we are "cloning", noting here that this is ONLY useful for tui_object's acting as tui_objects, heh
; see tui_spacers.inc for examples of why this is useful
falign
tui_object$simple_clone:
prolog tui_object$simple_clone
push rdi
mov edi, tui_object_size
call heap$alloc
mov rsi, [rsp]
mov rdi, rax
mov [rsp], rax
call tui_object$init_copy
pop rax
epilog
end if
if used tui_object$init_copy | defined include_everything
; two parameters: rdi == tui object we are initialising, rsi == source tui object
; this copies all static settings, creates new buffers (if width && height nonzero)
; copies the buffer contents, clones all children using the clone virtual method
; creates new dynamic contents... if displayname was non-null, creates a COPY of it
; (translation: displayname is always a dynamically allocated string if it exists)
falign
tui_object$init_copy:
prolog tui_object$init_copy
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+24], r15
mov edx, tui_object_size
call memcpy
xor rcx, rcx
mov rdi, [rsp]
mov rsi, [rsp+8]
mov [rdi+tui_text_ofs], rcx
mov [rdi+tui_attr_ofs], rcx
mov rdx, [rsi+tui_displayname_ofs]
mov [rdi+tui_focus_ofs], rcx
mov [rdi+tui_parent_ofs], rcx
mov [rdi+tui_displayname_ofs], rcx
test rdx, rdx
jz .nodisplayname
mov rdi, rdx
call string$copy
mov rdi, [rsp]
mov rsi, [rsp+8]
mov [rdi+tui_displayname_ofs], rax
calign
.nodisplayname:
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov rsi, [rsp+8]
mov [rdi+tui_bastards_ofs], rax
mov eax, [rsi+tui_width_ofs]
mov ecx, [rsi+tui_height_ofs]
test eax, eax
jz .nobuffers
test ecx, ecx
jz .nobuffers
mul ecx
shl eax, 2
mov dword [rsp+16], eax
mov edi, eax
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_text_ofs], rax
mov edi, dword [rsp+16]
call heap$alloc
mov rdi, [rsp]
mov rsi, [rsp+8]
mov [rdi+tui_attr_ofs], rax
mov edx, dword [rsp+16]
mov rdi, rax
mov rsi, [rsi+tui_attr_ofs]
call memcpy
mov rdi, [rsp]
mov rsi, [rsp+8]
mov edx, dword [rsp+16]
mov rdi, [rdi+tui_text_ofs]
mov rsi, [rsi+tui_text_ofs]
call memcpy
mov rdi, [rsp]
mov rsi, [rsp+8]
calign
.nobuffers:
; we have to iterate the list at [rsi+tui_children_ofs]
; and do vclones for every child there, add it to our own list
; and set each child's parent to rdi
; NOTE: we cheat here and use r15 across calls to list$foreach
; because we know for certain that r15 will remain intact
mov r15, rdi
mov rdi, [rsi+tui_children_ofs]
mov rsi, .childrencopy
call list$foreach
mov r15, [rsp+24]
add rsp, 32
epilog
calign
.childrencopy:
; called with a single argument of the tui_object
mov rsi, [rdi] ; load its vtable pointer
call qword [rsi+tui_vclone]
; so now we have an rax new cloned copy, we need to set its parent
mov [rax+tui_parent_ofs], r15 ; see comment above re: cheating
mov rdi, [r15+tui_children_ofs] ; ""
mov rsi, rax
call list$push_back
ret
end if
if used tui_object$init_rect | defined include_everything
; two arguments, rdi == tui_object we are initialising, and rsi: pointer to a bounds rect
falign
tui_object$init_rect:
prolog tui_object$init_rect
push rsi rdi ; not really preserved
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rax, [rsi]
mov rcx, [rsi+8]
mov [rdi+tui_bounds_ofs], rax
mov [rdi+tui_bounds_ofs+8], rcx
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
mov eax, [rdi+tui_bounds_bx_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
mov [rdi+tui_width_ofs], eax
mov ecx, [rdi+tui_bounds_by_ofs]
sub ecx, [rdi+tui_bounds_ay_ofs]
mov [rdi+tui_height_ofs], ecx
test eax, eax
jz .nobuffers
test ecx, ecx
jz .nobuffers
mul ecx
shl eax, 2
mov dword [rsp+8], eax
mov edi, eax
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_text_ofs], rax
xor esi, esi
mov edx, dword [rsp+8]
mov rdi, rax
call memset32
mov edi, dword [rsp+8]
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_attr_ofs], rax
xor esi, esi
mov edx, dword [rsp+8]
mov rdi, rax
call memset32
add rsp, 16
epilog
calign
.nobuffers:
add rsp, 16
epilog
end if
if used tui_object$init_id | defined include_everything
; three arguments: rdi == tui_object we are initialising, esi == width, xmm0 == heightperc
falign
tui_object$init_id: ; id == integer width, double height
prolog tui_object$init_id
movq rdx, xmm0
push rdx rsi rdi
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
mov rax, [rsp+8]
mov rcx, [rsp+16]
mov dword [rdi+tui_width_ofs], eax
mov [rdi+tui_heightperc_ofs], rcx
add rsp, 24
epilog
end if
if used tui_object$init_di | defined include_everything
; three arguments: rdi == tui_object we are initialising, xmm0 == widthperc, esi == height
falign
tui_object$init_di: ; id == double width, integer height
prolog tui_object$init_di
movq rdx, xmm0
push rdx rsi rdi
; [rsp] == tui_object
; [rsp+8] == height
; [rsp+16] == widthperc
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
mov rax, [rsp+8]
mov rcx, [rsp+16]
mov [rdi+tui_widthperc_ofs], rcx
mov dword [rdi+tui_height_ofs], eax
add rsp, 24
epilog
end if
if used tui_object$init_dd | defined include_everything
; three arguments: rdi == tui_object we are initialising, xmm0 == widthperc, xmm1 == heightperc
falign
tui_object$init_dd:
prolog tui_object$init_dd
movq rsi, xmm0
movq rdx, xmm1
push rdx rsi rdi
; [rsp] == tui_object
; [rsp+8] == widthperc
; [rsp+16] == heightperc
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
mov rax, [rsp+8]
mov rcx, [rsp+16]
mov [rdi+tui_widthperc_ofs], rax
mov [rdi+tui_heightperc_ofs], rcx
add rsp, 24
epilog
end if
if used tui_object$init_ii | defined include_everything
; three arguments: rdi == tui_object we are initialising, esi == width, edx == height
falign
tui_object$init_ii: ; ii == integer width, integer height
prolog tui_object$init_ii
push rdx rsi rdi
add rdi, 8 ; skip the vtable pointer
xor esi, esi
mov edx, tui_object_size - 8
call memset32
call list$new
mov rdi, [rsp]
mov [rdi+tui_children_ofs], rax
call list$new
mov rdi, [rsp]
mov [rdi+tui_bastards_ofs], rax
mov dword [rdi+tui_visible_ofs], 1
mov dword [rdi+tui_includeinlayout_ofs], 1
mov dword [rdi+tui_absolutex_ofs], -1
mov dword [rdi+tui_absolutey_ofs], -1
mov rax, [rsp+8]
mov rcx, [rsp+16]
mov dword [rdi+tui_width_ofs], eax
mov dword [rdi+tui_height_ofs], ecx
test eax, eax
jz .nobuffers
test ecx, ecx
jz .nobuffers
mul ecx
shl eax, 2
mov dword [rsp+8], eax
mov edi, eax
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_text_ofs], rax
xor esi, esi
mov edx, dword [rsp+8]
mov rdi, rax
call memset32
mov edi, dword [rsp+8]
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_attr_ofs], rax
xor esi, esi
mov edx, dword [rsp+8]
mov rdi, rax
call memset32
add rsp, 24
epilog
calign
.nobuffers:
add rsp, 24
epilog
end if
if used tui_object$cleanup | defined include_everything
; single argument: rdi == tui_object we are destroying
; NOTE: this does not free the pointer itself, but heap$free's everything that we allocated
; and iterates and does delete its children/bastards
falign
tui_object$cleanup:
prolog tui_object$cleanup
push rdi
mov rdi, [rdi+tui_children_ofs]
mov rsi, .childclear
call list$clear
mov rsi, [rsp]
mov rdi, [rsi+tui_children_ofs]
call heap$free
mov rsi, [rsp]
mov rdi, [rsi+tui_bastards_ofs]
mov rsi, .childclear
call list$clear
mov rsi, [rsp]
mov rdi, [rsi+tui_bastards_ofs]
call heap$free
mov rsi, [rsp]
mov rdi, [rsi+tui_text_ofs]
test rdi, rdi
jz .notextbuffer
call heap$free
mov rsi, [rsp]
; copy of notextbuffer fallthrough (most common will be no branching in here)
mov rdi, [rsi+tui_attr_ofs]
test rdi, rdi
jz .noattrbuffer
call heap$free
mov rsi, [rsp]
; copy of noattrbuffer fallthrough (most common will be no branching in here)
mov rdi, [rsi+tui_displayname_ofs]
test rdi, rdi
jnz .displayname
pop rdi
epilog
calign
.notextbuffer:
mov rdi, [rsi+tui_attr_ofs]
test rdi, rdi
jz .noattrbuffer
call heap$free
mov rsi, [rsp]
calign
.noattrbuffer:
mov rdi, [rsi+tui_displayname_ofs]
test rdi, rdi
jnz .displayname
pop rdi
epilog
calign
.displayname:
call heap$free
pop rdi
epilog
calign
.childclear:
; called for every child in children/bastards list
; we DO delete the pointer when we are done
push rdi ; save it so we can delete it
mov rsi, [rdi] ; load its vmethod table
call qword [rsi+tui_vcleanup] ; call its virtual cleanup method
pop rdi
call heap$free
ret
end if
if used tui_object$clone | defined include_everything
; single argument in rdi: our tui_object to make a copy of
; NOTE: ALL DESCENDENTS OF TUI_OBJECT MUST MAKE A VERSION OF THIS!
; also note: you must return a NEW heap$alloc'd version of your descendent
; initialized properly! (make sure to store your vtable!)
; default one listed here just generates a breakpoint, as it is definitely
; a programming error for the base one here to be called
falign
tui_object$clone:
prolog tui_object$clone
breakpoint
epilog
end if
if used tui_object$draw | defined include_everything
; single argument in rdi: our tui_object
; each descendent's draw method is responsible for updating its own text/attr buffers
; but the ACTUAL render routine (which is to say: the terminal/etc responsible for
; actually outputting the rendered version of the buffers) needs to be called in
; forward Z order, so if an object updates itself, it needs to call updatedisplaylist
; when it is done drawing its own buffers
; consequently, the default implementation listed here only calls the updatedisplaylist
; virtual method
falign
tui_object$draw:
prolog tui_object$draw
mov rsi, [rdi] ; load up our vtable
call qword [rsi+tui_vupdatedisplaylist]
epilog
end if
if used tui_object$redraw | defined include_everything
; single argument in rdi: our tui_object
; this is called basically telling all objects to draw themselves, and is done
; when layoutchanged happens, etc all initiated from our terminal/telnet/ssh/etc
; default is to call our own draw, and then call all of our children's redraw vmethods
falign
tui_object$redraw:
prolog tui_object$redraw
push rdi
mov rsi, [rdi] ; load up our vtable
call qword [rsi+tui_vdraw]
mov rdi, [rsp]
mov rdi, [rdi+tui_children_ofs]
mov rsi, .childredraw
call list$foreach
mov rdi, [rsp]
mov rdi, [rdi+tui_bastards_ofs]
mov rsi, .childredraw
call list$foreach
pop rdi
epilog
calign
.childredraw:
; called with a single argument in rdi of the child, we need to call its own redraw vmethod
mov rsi, [rdi] ; load up its vtable
call qword [rsi+tui_vredraw]
ret
end if
if used tui_object$updatedisplaylist | defined include_everything
; single argument in rdi: our tui_object
; this should not need to be overridden by anyone except top-most rendering layers
; default is to walk up the tree, calling each one's virtual updateDisplayList
; so that it can forward Z order walk back down and redraw/render everything
falign
tui_object$updatedisplaylist:
prolog tui_object$updatedisplaylist
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi] ; load up the parent's vtable
call qword [rsi+tui_vupdatedisplaylist]
epilog
calign
.noparent:
epilog
end if
if used tui_object$setcursor | defined include_everything
; three arguments: rdi == our tui_object, esi == x, edx == y
; default is to walk up the tree finding someone who cares
falign
tui_object$setcursor:
prolog tui_object$setcursor
; if we are scrolled, and a lower object set the cursor with its own bounds.a
; we need to _adjust_ by our scroll amounts
mov eax, [rdi+tui_scroll_ofs]
mov ecx, [rdi+tui_scroll_ofs+4]
sub esi, eax
sub edx, ecx
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rcx, [rdi] ; load up the parent's vtable
call qword [rcx+tui_vsetcursor]
epilog
calign
.noparent:
epilog
end if
if used tui_object$showcursor | defined include_everything
; single argument in rdi: our tui_object
; default is to walk up the tree finding someone who cares
falign
tui_object$showcursor:
prolog tui_object$showcursor
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi]
call qword [rsi+tui_vshowcursor]
epilog
calign
.noparent:
epilog
end if
if used tui_object$hidecursor | defined include_everything
; single argument in rdi: our tui_object
; default is to walk up the tree finding someone who cares
falign
tui_object$hidecursor:
prolog tui_object$hidecursor
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi]
call qword [rsi+tui_vhidecursor]
epilog
calign
.noparent:
epilog
end if
if used tui_object$timer | defined include_everything
; single argument in rdi: our tui_object
; this should be overridden only if you, the descendent, is interested in timer events
; whatever resolution you asked for, your vmethod of same will get called
; NOTE: if you return nonzero in eax from this, your object will be automatically
; destroyed (useful for set and forget type situations)
falign
tui_object$timer:
prolog tui_object$timer
xor eax, eax ; keep the timer going is the default action
epilog
end if
if used tui_object$sizechanged | defined include_everything
; single argument in rdi: our tui_object
; this is called if our layout goods actually modified our width/height,
; in which case, we destroy our text/attr buffers if we have them
; and reallocate them if our new size is nonzero
; and if new size is nonzero, we also call our virtual draw method
falign
tui_object$sizechanged:
prolog tui_object$sizechanged
sub rsp, 8
push rdi
mov rdi, [rdi+tui_text_ofs]
test rdi, rdi
jz .notextbuffer
call heap$free
mov rdi, [rsp]
mov qword [rdi+tui_text_ofs], 0
mov rdi, [rdi+tui_attr_ofs]
test rdi, rdi
jz .noattrbuffer
call heap$free
mov rdi, [rsp]
mov qword [rdi+tui_attr_ofs], 0
calign
.checknew:
mov eax, [rdi+tui_width_ofs]
mov ecx, [rdi+tui_height_ofs]
test eax, eax
jz .nonewone
test ecx, ecx
jz .nonewone
mul ecx
shl eax, 2
mov dword [rsp+8], eax
mov edi, eax
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_text_ofs], rax
mov rdi, rax
xor esi, esi
mov edx, dword [rsp+8]
call memset32
mov edi, dword [rsp+8]
call heap$alloc
mov rdi, [rsp]
mov [rdi+tui_attr_ofs], rax
mov rdi, rax
xor esi, esi
mov edx, dword [rsp+8]
call memset32
; so now our buffers have been allocated
; we have to call our virtual draw method
mov rdi, [rsp]
mov rsi, [rdi] ; load up our vtable
call qword [rsi+tui_vdraw]
pop rdi
add rsp, 8
epilog
calign
.notextbuffer:
mov rdi, [rsp]
mov rdi, [rdi+tui_attr_ofs]
test rdi, rdi
jz .noattrbuffer
call heap$free
mov rdi, [rsp]
mov qword [rdi+tui_attr_ofs], 0
jmp .checknew
calign
.noattrbuffer:
mov rdi, [rsp]
jmp .checknew
calign
.nonewone:
pop rdi
add rsp, 8
epilog
end if
if used tui_object$layoutchanged | defined include_everything
; single argument in rdi: our tui_object
; this should not need to be overridden by anyone except topmost objects (terminal, renderers, etc)
; default action is to walk up the tree calling each one's
falign
tui_object$layoutchanged:
prolog tui_object$layoutchanged
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi] ; load up the parent's vtable
call qword [rsi+tui_vlayoutchanged]
epilog
calign
.noparent:
epilog
end if
if used tui_object$move | defined include_everything
; three arguments: rdi == our tui_object, esi == delta x, edx == delta y
; default action is:
; for absolute-positioned elements, updates absolute_x and absolute_y
; and updates the bounds rect for all
falign
tui_object$move:
prolog tui_object$move
add dword [rdi+tui_bounds_ax_ofs], esi
add dword [rdi+tui_bounds_ay_ofs], edx
add dword [rdi+tui_bounds_bx_ofs], esi
add dword [rdi+tui_bounds_by_ofs], edx
cmp dword [rdi+tui_absolutex_ofs], -1
jne .absolutetoo
epilog
calign
.absolutetoo:
add dword [rdi+tui_absolutex_ofs], esi
add dword [rdi+tui_absolutey_ofs], edx
epilog
end if
if used tui_object$setfocus | defined include_everything
; two arguments: rdi == our tui_object, rsi == the object that got focus
; default here is just to set the focus qword
falign
tui_object$setfocus:
prolog tui_object$setfocus
mov [rdi+tui_focus_ofs], rsi
epilog
end if
if used tui_object$gotfocus | defined include_everything
; single argument in rdi: our tui_object
falign
tui_object$gotfocus:
prolog tui_object$gotfocus
epilog
end if
if used tui_object$lostfocus | defined include_everything
; single argument in rdi: our tui_object
falign
tui_object$lostfocus:
prolog tui_object$lostfocus
epilog
end if
if used tui_object$keyevent | defined include_everything
; three arguments: rdi == our tui_object, esi == key, edx == esc_key
; NOTE: return zero in eax if firekeyevent should keep walking up "bubbling"
; and return nonzero in eax if firekeyevent should STOP
falign
tui_object$keyevent:
prolog tui_object$keyevent
xor eax, eax
epilog
end if
if used tui_object$domodal | defined include_everything
; two arguments: rdi == our tui_object, rsi == child object who wants modal
; default action here is to walk up the chain until we find someone who cares
; makes it easy for children who have no idea who their daddy is to get some
; action, haha
falign
tui_object$domodal:
prolog tui_object$domodal
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rdx, [rdi] ; load up the parent's vtable
; rsi child argument still intact
call qword [rdx+tui_vdomodal]
epilog
calign
.noparent:
epilog
end if
if used tui_object$endmodal | defined include_everything
; single argument: rdi == our tui_object
; default action here similar to domodal is just to walk up the chain until
; we find someone who cares
falign
tui_object$endmodal:
prolog tui_object$endmodal
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rdx, [rdi] ; load up the parent's vtable
call qword [rdx+tui_vendmodal]
epilog
calign
.noparent:
epilog
end if
if used tui_object$exit | defined include_everything
; two arguments: rdi == our tui_object, esi == exit code
; default action here is to walk up the tree until we find someone who cares
; and depending on who that real parent context is will determine what actually
; happens, e.g. drop connection, syscall_exit, etc
falign
tui_object$exit:
prolog tui_object$exit
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rdx, [rdi]
; esi argument still in tact
call qword [rdx+tui_vexit]
epilog
calign
.noparent:
epilog
end if
if used tui_object$calcbounds | defined include_everything
; single argument in rdi: our tui_object
; default action here should work for most descendent cases
falign
tui_object$calcbounds:
prolog tui_object$calcbounds
push rdi
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparentyet
mov rdx, [rdi] ; load up the parent's vtable
call qword [rdx+tui_vcalcchildbounds]
; now do the same for all our children
mov rdi, [rsp]
mov rdi, [rdi+tui_children_ofs]
mov rsi, .childbounds
call list$foreach
mov rdi, [rsp]
mov rdi, [rdi+tui_bastards_ofs]
mov rsi, .childbounds
call list$foreach
pop rdi
epilog
calign
.childbounds:
; called for every child, we have to call its virtual calcbounds
mov rsi, [rdi] ; load up our vtable
call qword [rsi+tui_vcalcbounds]
ret
calign
.noparentyet:
; if we dont have a parent yet, we haven't been added to anyone WITH bounds yet
; so we bailout
pop rdi
epilog
end if
if used tui_object$calcchildbounds | defined include_everything
; single argument in rdi: our tui_object
; default action here should work for most descendent cases
; this is responsible for all our fine layout calculations
falign
tui_object$calcchildbounds:
prolog tui_object$calcchildbounds
push rdi
mov rdx, rdi
mov rdi, [rdi+tui_bastards_ofs]
mov rsi, .bastardsfirst
call list$foreach_arg
mov rdi, [rsp]
mov eax, [rdi+tui_layout_ofs]
cmp eax, tui_layout_absolute
je .absolutelayout
cmp eax, tui_layout_vertical
je .verticallayout
; else, we have a horizontal layout
cmp dword [rdi+tui_width_ofs], 0
jle .noroom
; NOTE: we cheat here and use r14 and r15 across calls to list$foreach
; because we know for certain that r14 and r15 will remain intact
push r14 r15
mov r14, rdi
mov r15d, dword [rdi+tui_width_ofs] ; available width
xorpd xmm0, xmm0 ; total width percent
mov rdi, [rdi+tui_children_ofs]
mov rsi, .horizsizescan
call list$foreach
mov rdx, r14
xor r14d, r14d ; relative x
mov rdi, [rdx+tui_children_ofs]
mov rsi, .horizsizeset
call list$foreach_arg
pop r15 r14
pop rdi
epilog
calign
.absolutelayout:
mov rdx, rdi
mov rdi, [rdi+tui_children_ofs]
mov rsi, .childabsolute
call list$foreach_arg
pop rdi
epilog
calign
.noroom:
pop rdi
epilog
calign
.verticallayout:
; NOTE: we cheat here and use r14 and r15 across calls to list$foreach
; because we know for certain that r14 and r15 will remain intact
cmp dword [rdi+tui_height_ofs], 0
jle .noroom
push r14 r15
mov r14, rdi
mov r15d, dword [rdi+tui_height_ofs] ; available height
xorpd xmm0, xmm0 ; total height percent
mov rdi, [rdi+tui_children_ofs]
mov rsi, .vertsizescan
call list$foreach
mov rdx, r14
xor r14d, r14d ; relative y
mov rdi, [rdx+tui_children_ofs]
mov rsi, .vertsizeset
call list$foreach_arg
pop r15 r14
pop rdi
epilog
calign
.vertsizeset:
; so rdi is our child object, rsi is our parent object, r14d == relative_y, r15d == available_height, xmm0 == total_heightperc
; the combination of visible/includeinlayout means that calcbounds will still apply to invisible children (useful for effects, etc)
; cmp dword [rdi+tui_visible_ofs], 0
; je .vertsizeset_nodeal
cmp dword [rdi+tui_includeinlayout_ofs], 0
je .vertsizeset_nodeal
mov r10d, [rdi+tui_width_ofs] ; old child width
mov r11d, [rdi+tui_height_ofs] ; old child height
cmp qword [rdi+tui_widthperc_ofs], 0
je .vertsizeset_checkheightperc
; else, widthperc is nonzero, so this childs' width relative to its parent
mov eax, [rsi+tui_width_ofs]
movq xmm1, [rdi+tui_widthperc_ofs]
movq xmm3, [.math_hundredth]
cvtsi2sd xmm2, eax
mulsd xmm1, xmm3 ; turn the widthperc back into a real percentage
mulsd xmm1, xmm2 ; multiplied by parent width
cvtsd2si eax, xmm1
mov [rdi+tui_width_ofs], eax ; and back as an integer
calign
.vertsizeset_checkheightperc:
cmp qword [rdi+tui_heightperc_ofs], 0
je .vertsizeset_setbounds
; else, heightperc is nonzero, so this calculate its relative goods
cvtsi2sd xmm2, r15d ; convert available_height to a double
movq xmm1, [rdi+tui_heightperc_ofs]
divsd xmm1, xmm0 ; /= total height percent
mulsd xmm1, xmm2 ; *= available height
cvtsd2si eax, xmm1
; it is possible that there wasn't enough room, so if eax < 0, set to zero
xor r8d, r8d
cmp eax, 0
cmovl eax, r8d
mov [rdi+tui_height_ofs], eax ; and back as an integer
calign
.vertsizeset_setbounds:
xor edx, edx ; remainder
mov r8d, [rsi+tui_width_ofs] ; parent width
mov eax, [rdi+tui_width_ofs] ; child width
sub r8d, eax ; parent width - child width
; in case parent width was less than child width, make sure we don't end up with negative numbers
cmp r8d, 0
cmovl r8d, edx
mov r9d, r8d
shr r9d, 1
cmp dword [rsi+tui_horizalign_ofs], tui_align_right
cmove edx, r8d
cmp dword [rsi+tui_horizalign_ofs], tui_align_center
cmove edx, r9d
mov eax, [rsi+tui_bounds_ax_ofs] ; parent bounds.a.x
add eax, edx ; + remainder
mov [rdi+tui_bounds_ax_ofs], eax ; child bounds.a.x =
add eax, dword [rdi+tui_width_ofs] ; + child width
mov [rdi+tui_bounds_bx_ofs], eax
mov eax, [rsi+tui_bounds_ay_ofs] ; parent bounds.a.y
add eax, r14d ; + relative_y
mov [rdi+tui_bounds_ay_ofs], eax ; child bounds.a.y =
add eax, dword [rdi+tui_height_ofs] ; + child height
mov [rdi+tui_bounds_by_ofs], eax ; child bounds.b.y =
add r14d, dword [rdi+tui_height_ofs]
; it is possible that due to rounding errors, we went one too far, check to make sure we didn't overstep the bounds
cmp r14d, [rsi+tui_height_ofs]
ja .vertsizeset_overrun
calign
.vertsizeset_next:
cmp r10d, dword [rdi+tui_width_ofs]
jne .firesizechanged
cmp r11d, dword [rdi+tui_height_ofs]
jne .firesizechanged
ret
calign
.vertsizeset_overrun:
; only modify this if we actually did a height percentage:
cmp qword [rdi+tui_heightperc_ofs], 0
je .vertsizeset_next
; only modify if already >0
cmp dword [rdi+tui_height_ofs], 0
jle .vertsizeset_next
sub dword [rdi+tui_height_ofs], 1
sub r14d, 1
cmp r10d, dword [rdi+tui_width_ofs]
jne .firesizechanged
cmp r11d, dword [rdi+tui_height_ofs]
jne .firesizechanged
ret
calign
.math_hundredth dq 0.01f
calign
.firesizechanged:
mov rsi, [rdi] ; load up the vtable
sub rsp, 8
movq [rsp], xmm0 ; preserve total percentage
call qword [rsi+tui_vsizechanged]
movq xmm0, [rsp]
add rsp, 8
ret
calign
.vertsizescan:
; see note above re: cheating with r15, we know r15 and xmm0 stay in tact across list$foreach
; cmp dword [rdi+tui_visible_ofs], 0
; je .vertsizescan_nodeal
cmp dword [rdi+tui_includeinlayout_ofs], 0
je .vertsizescan_nodeal
cmp qword [rdi+tui_heightperc_ofs], 0
jne .vertsizescan_percbased
sub r15d, dword [rdi+tui_height_ofs]
ret
calign
.vertsizescan_percbased:
movq xmm1, [rdi+tui_heightperc_ofs]
addsd xmm0, xmm1
ret
calign
.vertsizescan_nodeal:
.vertsizeset_nodeal:
.horizsizescan_nodeal:
.horizsizeset_nodeal:
ret
calign
.horizsizeset:
; cmp dword [rdi+tui_visible_ofs], 0
; je .horizsizeset_nodeal
cmp dword [rdi+tui_includeinlayout_ofs], 0
je .horizsizeset_nodeal
mov r10d, [rdi+tui_width_ofs] ; old child width
mov r11d, [rdi+tui_height_ofs] ; old child height
cmp qword [rdi+tui_widthperc_ofs], 0
je .horizsizeset_checkheightperc
cvtsi2sd xmm2, r15d ; convert available_width to a double
movq xmm1, [rdi+tui_widthperc_ofs]
divsd xmm1, xmm0 ; /= total width percent
mulsd xmm1, xmm2 ; *= available width
cvtsd2si eax, xmm1
; it is possible that there wasn't enough room, so if eax < 0, set to zero
xor r8d, r8d
cmp eax, 0
cmovl eax, r8d
mov [rdi+tui_width_ofs], eax ; and back as an integer
calign
.horizsizeset_checkheightperc:
cmp qword [rdi+tui_heightperc_ofs], 0
je .horizsizeset_setbounds
mov eax, [rsi+tui_height_ofs]
movq xmm1, [rdi+tui_heightperc_ofs]
movq xmm3, [.math_hundredth]
cvtsi2sd xmm2, eax
mulsd xmm1, xmm3 ; turn the heightperc back into a real percentage
mulsd xmm1, xmm2 ; multiplied by parent height
cvtsd2si eax, xmm1
mov [rdi+tui_height_ofs], eax ; and back as an integer
calign
.horizsizeset_setbounds:
xor edx, edx ; remainder
mov r8d, [rsi+tui_height_ofs] ; parent height
mov eax, [rdi+tui_height_ofs] ; child height
sub r8d, eax ; parent height - child height
; in case parent height was less than child height, make sure we don't end up with negative numbers
cmp r8d, 0
cmovl r8d, edx
mov r9d, r8d
shr r9d, 1
cmp dword [rsi+tui_vertalign_ofs], tui_align_bottom
cmove edx, r8d
cmp dword [rsi+tui_vertalign_ofs], tui_align_middle
cmove edx, r9d
mov eax, [rsi+tui_bounds_ax_ofs] ; parent bounds.a.x
add eax, r14d ; + relative_x
mov [rdi+tui_bounds_ax_ofs], eax ; child bounds.a.x =
add eax, [rdi+tui_width_ofs] ; + child width
mov [rdi+tui_bounds_bx_ofs], eax ; child bounds.b.x =
mov eax, [rsi+tui_bounds_ay_ofs] ; parent bounds.a.y
add eax, edx ; + remainder
mov [rdi+tui_bounds_ay_ofs], eax ; child bounds.a.y =
add eax, [rdi+tui_height_ofs] ; + child height
mov [rdi+tui_bounds_by_ofs], eax ; child bounds.b.y =
add r14d, dword [rdi+tui_width_ofs] ; relative_x += child width
; it is possible that due to rounding errors, we went one too far, check to make sure we didn't overstep the bounds
cmp r14d, [rsi+tui_width_ofs]
ja .horizsizeset_overrun
calign
.horizsizeset_next:
cmp r10d, dword [rdi+tui_width_ofs]
jne .firesizechanged
cmp r11d, dword [rdi+tui_height_ofs]
jne .firesizechanged
ret
calign
.horizsizeset_overrun:
; only modify this if we actually did a width percentage:
cmp qword [rdi+tui_widthperc_ofs], 0
je .horizsizeset_next
; only modify if already >0
cmp dword [rdi+tui_width_ofs], 0
jle .vertsizeset_next
sub dword [rdi+tui_width_ofs], 1
sub r14d, 1
cmp r10d, dword [rdi+tui_width_ofs]
jne .firesizechanged
cmp r11d, dword [rdi+tui_height_ofs]
jne .firesizechanged
ret
calign
.horizsizescan:
; see note above re: cheating with r15
; cmp dword [rdi+tui_visible_ofs], 0
; je .horizsizescan_nodeal
cmp dword [rdi+tui_includeinlayout_ofs], 0
je .horizsizescan_nodeal
cmp qword [rdi+tui_widthperc_ofs], 0
jne .horizsizescan_percbased
sub r15d, dword [rdi+tui_width_ofs]
ret
calign
.horizsizescan_percbased:
movq xmm1, [rdi+tui_widthperc_ofs]
addsd xmm0, xmm1
ret
calign
.bastardsfirst:
; for our bastard children, we really only need to take into account whether
; bastardglue is set or not and modify/adjust their position
; so our argument in rdi is the bastard child, rsi is our parent
cmp dword [rdi+tui_bastardglue_ofs], 0
je .nobastardglue
; otehrwise, bastardglue is set, so pay attention to its
; vertical and horizontal alignment to set its x/y nothing that we do NOT
; modify its SIZE.
xor edx, edx ; remainder
mov r8d, [rsi+tui_width_ofs] ; parent width
mov r9d, [rdi+tui_width_ofs] ; child width
sub r8d, r9d ; parent width - child width
; make sure we didn't go negative
cmp r8d, 0
cmovl r8d, edx
mov r10d, r8d
shr r10d, 1
cmp dword [rdi+tui_horizalign_ofs], tui_align_right
cmove edx, r8d
cmp dword [rdi+tui_horizalign_ofs], tui_align_center
cmove edx, r10d
mov eax, [rsi+tui_bounds_ax_ofs]
add eax, edx
mov [rdi+tui_bounds_ax_ofs], eax
add eax, [rdi+tui_width_ofs]
mov [rdi+tui_bounds_bx_ofs], eax
xor edx, edx ; remainder
mov r8d, [rsi+tui_height_ofs] ; parent height
mov r9d, [rdi+tui_height_ofs] ; child height
sub r8d, r9d ; parent height - child height
; make sure we didn't go negative
cmp r8d, 0
cmovl r8d, edx
mov r10d, r8d
shr r10d, 1
cmp dword [rdi+tui_vertalign_ofs], tui_align_bottom
cmove edx, r8d
cmp dword [rdi+tui_vertalign_ofs], tui_align_middle
cmove edx, r10d
mov eax, [rsi+tui_bounds_ay_ofs]
add eax, edx
mov [rdi+tui_bounds_ay_ofs], eax
add eax, [rdi+tui_height_ofs]
mov [rdi+tui_bounds_by_ofs], eax
ret
calign
.nobastardglue:
.childabsolute: ; the code for unglued bastard absolute layouts is the same
; its position is relative to ours, so we treat it as an absolute layout
cmp dword [rdi+tui_absolutex_ofs], -1
je .nobastardglue_first
mov eax, [rsi+tui_bounds_ax_ofs] ; parent.a.x
add eax, [rdi+tui_absolutex_ofs] ; + child.absolutex
mov ecx, [rsi+tui_bounds_ay_ofs] ; parent.a.y
add ecx, [rdi+tui_absolutey_ofs] ; + child.absolutey
mov [rdi+tui_bounds_ax_ofs], eax ; child.a.x =
mov [rdi+tui_bounds_ay_ofs], ecx ; child.a.y =
add eax, [rdi+tui_width_ofs] ; + child width
add ecx, [rdi+tui_height_ofs] ; + child height
mov [rdi+tui_bounds_bx_ofs], eax
mov [rdi+tui_bounds_by_ofs], ecx
ret
calign
.nobastardglue_first:
; its position is still relative, but we haven't yet set absolute_x
mov eax, [rdi+tui_bounds_ax_ofs]
mov ecx, [rdi+tui_bounds_ay_ofs]
mov [rdi+tui_absolutex_ofs], eax
mov [rdi+tui_absolutey_ofs], ecx
; now same as above:
mov eax, [rsi+tui_bounds_ax_ofs]
add eax, [rdi+tui_absolutex_ofs]
mov ecx, [rsi+tui_bounds_ay_ofs]
add ecx, [rdi+tui_absolutey_ofs]
mov [rdi+tui_bounds_ax_ofs], eax
mov [rdi+tui_bounds_ay_ofs], ecx
add eax, [rdi+tui_width_ofs]
add ecx, [rdi+tui_height_ofs]
mov [rdi+tui_bounds_bx_ofs], eax
mov [rdi+tui_bounds_by_ofs], ecx
ret
end if
if used tui_object$appendchild | defined include_everything
; two arguments: rdi == our tui_object, rsi == new object to add
; we set the new object's parent to our rdi, add it to our children list, and then call our vmethod layoutchanged
falign
tui_object$appendchild:
prolog tui_object$appendchild
push rdi
mov [rsi+tui_parent_ofs], rdi
mov rdi, [rdi+tui_children_ofs]
call list$push_back
mov rdi, [rsp]
mov rsi, [rdi] ; load our vtable
call qword [rsi+tui_vlayoutchanged] ; force a redraw
pop rdi
epilog
end if
if used tui_object$appendbastard | defined include_everything
; two arguments: rdi == our tui_object, rsi == new object to add
; we set the new object's parent to our rdi, add it to our bastards list, and then call our vmethod layoutchanged
falign
tui_object$appendbastard:
prolog tui_object$appendbastard
push rdi
mov [rsi+tui_parent_ofs], rdi
mov rdi, [rdi+tui_bastards_ofs]
call list$push_back
mov rdi, [rsp]
mov rsi, [rdi] ; load our vtable
call qword [rsi+tui_vlayoutchanged] ; force a redraw
pop rdi
epilog
end if
if used tui_object$prependchild | defined include_everything
; same as appendchild, but sticks it at the front of the list instead of the end
falign
tui_object$prependchild:
prolog tui_object$prependchild
push rdi
mov [rsi+tui_parent_ofs], rdi
mov rdi, [rdi+tui_children_ofs]
call list$push_front
mov rdi, [rsp]
mov rsi, [rdi] ; load our vtable
call qword [rsi+tui_vlayoutchanged] ; force a redraw
pop rdi
epilog
end if
if used tui_object$contains | defined include_everything
; two arguments: rdi == our tui_object, rsi == object to test for
; returns zero in eax if rsi is not in rdi's children list, or 1 in eax if it does
falign
tui_object$contains:
prolog tui_object$contains
mov rdi, [rdi+tui_children_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .zeroret
calign
.loop:
cmp rsi, [rdx+_list_valueofs]
je .oneret
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .loop
xor eax, eax
epilog
calign
.zeroret:
xor eax, eax
epilog
calign
.oneret:
mov eax, 1
epilog
end if
if used tui_object$getchildindex | defined include_everything
; two arguments: rdi == our tui_object, rsi == object to test for
; returns unsigned index in eax, or -1 if it isn't found
falign
tui_object$getchildindex:
prolog tui_object$getchildindex
xor eax, eax
mov rdi, [rdi+tui_children_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .negoneret
calign
.loop:
cmp rsi, [rdx+_list_valueofs]
je .oneret
add eax, 1
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .loop
mov eax, -1
epilog
calign
.negoneret:
mov eax, -1
epilog
calign
.oneret:
epilog
end if
if used tui_object$removechild | defined include_everything
; two arguments: rdi == our tui_object, rsi == object to remove
; NOTE: does _NOT_ delete/cleanup/heap$free the child
; if we do indeed remove it, we fire a layoutchanged vmethod call to redraw
; returns zero in rax if we didn't find it, or returns the child if we did
falign
tui_object$removechild:
prolog tui_object$removechild
push rdi ; save our actual object
mov rdi, [rdi+tui_children_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .zeroret
calign
.loop:
cmp rsi, [rdx+_list_valueofs]
je .foundit
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .loop
xor eax, eax
pop rdi
epilog
calign
.zeroret:
pop rdi
xor eax, eax
epilog
calign
.foundit:
push rsi ; save our return
mov rsi, rdx
call list$remove
pop rax
pop rdi ; get our object back
mov rsi, [rdi] ; load the vtable
push rax ; preserve our return
call qword [rsi+tui_vlayoutchanged]
pop rax
epilog
end if
if used tui_object$removebastard | defined include_everything
; two arguments: rdi == our tui_object, rsi == object to remove
; same as removechild, only does so with the bastards list instead
falign
tui_object$removebastard:
prolog tui_object$removebastard
push rdi ; save our actual object
mov rdi, [rdi+tui_bastards_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .zeroret
calign
.loop:
cmp rsi, [rdx+_list_valueofs]
je .foundit
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .loop
xor eax, eax
pop rdi
epilog
calign
.zeroret:
pop rdi
xor eax, eax
epilog
calign
.foundit:
push rsi ; save our return
mov rsi, rdx
call list$remove
pop rax
pop rdi ; get our object back
mov rsi, [rdi] ; load the vtable
push rax ; preserve our return
call qword [rsi+tui_vlayoutchanged]
pop rax
epilog
end if
if used tui_object$removeallchildren | defined include_everything
; single argument: rdi == our tui_object
; removes ALL children, and if we actually deleted any, fires a new layoutchanged vmethod call
; note: this actually DOES delete them (after calling vcleanup first, then it heap$frees them)
falign
tui_object$removeallchildren:
prolog tui_object$removeallchildren
push rdi
mov rdi, [rdi+tui_children_ofs]
cmp [_list_first], 0
je .nothingtodo
mov rsi, .childkill
call list$clear
mov rdi, [rsp]
mov rsi, [rdi] ; load the vtable
call qword [rsi+tui_vlayoutchanged]
pop rdi
epilog
calign
.childkill:
; single argument in rdi == the child we are killing
push rdi
mov rsi, [rdi]
call qword [rsi+tui_vcleanup]
pop rdi
call heap$free
ret
calign
.nothingtodo:
pop rdi
epilog
end if
if used tui_object$removeallbastards | defined include_everything
; single argument: rdi == our tui_object
; removes ALL bastards, and if we actually deleted any, fires a new layoutchanged vmethod call
; note: this actually DOES delete them (after calling vcleanup first, then it heap$frees them)
falign
tui_object$removeallbastards:
prolog tui_object$removeallbastards
push rdi
mov rdi, [rdi+tui_bastards_ofs]
cmp [_list_first], 0
je .nothingtodo
mov rsi, .childkill
call list$clear
mov rdi, [rsp]
mov rsi, [rdi] ; load the vtable
call qword [rsi+tui_vlayoutchanged]
pop rdi
epilog
calign
.childkill:
; single argument in rdi == the child we are killing
push rdi
mov rsi, [rdi]
call qword [rsi+tui_vcleanup]
pop rdi
call heap$free
ret
calign
.nothingtodo:
pop rdi
epilog
end if
if used tui_object$getobjectsunderpoint | defined include_everything
; three arguments: rdi == our tui_object, rsi == a POINTER to a point, rdx == list to append objects to
falign
tui_object$getobjectsunderpoint:
prolog tui_object$getobjectsunderpoint
push rax rdx rsi rdi
mov rdi, [rdi+tui_children_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .checkbastards
calign
.loop:
mov r8, [rdx+_list_valueofs] ; get the child itself
; determine if this child's bounds contains the point pointed to by rsi
mov eax, [rsi] ; point.x
mov ecx, [rsi+4] ; point.y
cmp eax, dword [r8+tui_bounds_ofs]
jl .nope
cmp eax, dword [r8+tui_bounds_ofs+8]
jge .nope
cmp ecx, dword [r8+tui_bounds_ofs+4]
jl .nope
cmp ecx, dword [r8+tui_bounds_ofs+12]
jge .nope
; else, the point is in bounds, so we need to add it to our list
mov [rsp+24], rdx ; save our list pointer so we can keep going
mov rdi, [rsp+16] ; the destination list
mov rsi, r8 ; the child to add
call list$push_back
; next, we need to call this child's vgetobjectsunderpoint
mov rdx, [rsp+24]
mov rdi, [rdx+_list_valueofs] ; the object
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov rcx, [rdi] ; its vtable
call qword [rcx+tui_vgetobjectsunderpoint]
mov rdx, [rsp+24]
mov rsi, [rsp+8]
calign
.nope:
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .loop
; else, restore our rdi
mov rdi, [rsp]
calign
.checkbastards:
mov rdi, [rdi+tui_bastards_ofs]
mov rdx, [_list_first]
test rdx, rdx
jz .alldone
calign
.bloop:
mov r8, [rdx+_list_valueofs] ; get the child itself
; determine if this child's bounds contains the point pointed to by rsi
mov eax, [rsi] ; point.x
mov ecx, [rsi+4] ; point.y
cmp eax, dword [r8+tui_bounds_ofs]
jl .bnope
cmp eax, dword [r8+tui_bounds_ofs+8]
jge .bnope
cmp ecx, dword [r8+tui_bounds_ofs+4]
jl .bnope
cmp ecx, dword [r8+tui_bounds_ofs+12]
jge .bnope
; else, the point is in bounds, so we need to add it to our list
mov [rsp+24], rdx ; save our list pointer so we can keep going
mov rdi, [rsp+16] ; the destination list
mov rsi, r8 ; the child to add
call list$push_back
; next, we need to call this child's vgetobjectsunderpoint
mov rdx, [rsp+24]
mov rdi, [rdx+_list_valueofs] ; the object
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov rcx, [rdi] ; its vtable
call qword [rcx+tui_vgetobjectsunderpoint]
mov rdx, [rsp+24]
mov rsi, [rsp+8]
calign
.bnope:
mov rdx, [rdx+_list_nextofs]
test rdx, rdx
jnz .bloop
; else, we are all done
add rsp, 32
epilog
calign
.alldone:
add rsp, 32
epilog
end if
if used tui_object$nvflattenchild | defined include_everything
; four arguments: rdi == our tui_object, rsi == fresh text buffer, rdx == fresh attr buffer, rcx == child to flatten into it
; NOTE: it is called nvflattenchild because this one is _not_ a member of the virtual method table
falign
tui_object$nvflattenchild:
prolog tui_object$nvflattenchild
mov rax, [rdi+tui_bounds_ofs]
mov r8, [rdi+tui_bounds_ofs+8]
mov r10d, [rcx+tui_bounds_ofs] ; child bounds.a.x
mov r11d, [rcx+tui_bounds_ofs+8] ; child bounds.b.x
; first up, make a clipping rect
sub rsp, 136
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
mov [rsp+24], rcx
mov [rsp+32], rax ; our bounds.a
mov [rsp+40], r8 ; our bounds.b
; intersect this temporary bounds object with the child's bounds
; rdi == rsp+32
; rsi == [rcx+tui_bounds_ofs]
mov r8d, [rsp+32] ; our bounds.a.x
mov r9d, [rsp+40] ; our bounds.b.x
; scroll mod:
add r8d, [rdi+tui_scroll_ofs]
add r9d, [rdi+tui_scroll_ofs]
; end scroll mod
cmp r8d, r10d
cmovl r8d, r10d
cmp r9d, r11d
cmovg r9d, r11d
mov [rsp+32], r8d ; intersected with child's a.x
mov [rsp+40], r9d ; intersected with child's b.x
mov r8d, [rsp+36] ; our bounds.a.y
mov r9d, [rsp+44] ; our bounds.b.y
; scroll mod:
add r8d, [rdi+tui_scroll_ofs+4]
add r9d, [rdi+tui_scroll_ofs+4]
; end scroll mod
mov r10d, [rcx+tui_bounds_ofs+4]
mov r11d, [rcx+tui_bounds_ofs+12]
cmp r8d, r10d
cmovl r8d, r10d
cmp r9d, r11d
cmovg r9d, r11d
mov [rsp+36], r8d
mov [rsp+44], r9d
; check to see if our intersection is empty
mov r8d, [rsp+32]
mov r9d, [rsp+36]
cmp r8d, [rsp+40]
jge .bailout
cmp r9d, [rsp+44]
jge .bailout
; otherwise, it is non-empty, proceed:
mov qword [rsp+128], 0
; if scroll is nonzero, and we clipped the child, we need to scroll the child cliprect too
cmp qword [rdi+tui_scroll_ofs], 0
je .notempty_noscroll
cmp dword [rdi+tui_scroll_ofs], 0
je .notempty_noxscroll
; otherwise, xscroll is nonzero, see if we got all of our child's bounds
mov r10d, [rcx+tui_bounds_ofs]
mov r11d, [rcx+tui_bounds_ofs+8]
sub r11d, r10d
; that now has our child width, check that against our clipped width
mov r9d, [rsp+40]
mov r8d, [rsp+32]
sub r9d, r8d
cmp r11d, r9d
je .notempty_noxscroll
; otherwise, the difference (r11d - r9d) must be the cliprect scroll x
sub r11d, r9d
; if cliprect.a.x == child's bounds.a.x, no scroll
cmp r10d, [rsp+32]
je .notempty_noxscroll
mov [rsp+128], r11d
calign
.notempty_noxscroll:
; do the same for y
cmp dword [rdi+tui_scroll_ofs+4], 0
je .notempty_noscroll
; otherwise, yscroll is nonzero, see if we got all of our child's bounds
mov r10d, [rcx+tui_bounds_ofs+4]
mov r11d, [rcx+tui_bounds_ofs+12]
sub r11d, r10d
; that now has our child height, check that against our clipped height
mov r9d, [rsp+44]
mov r8d, [rsp+36]
sub r9d, r8d
cmp r11d, r9d
je .notempty_noscroll
; otherwise, the difference (r11d - r9d) must be the cliprect scroll y
sub r11d, r9d
; if cliprect.a.y == child's bounds.a.y, no scroll
cmp r10d, [rsp+36]
je .notempty_noscroll
mov [rsp+132], r11d
calign
.notempty_noscroll:
; next up: call our child's flatten vmethod
mov rdi, rcx
mov rsi, [rdi]
call qword [rsi+tui_vflatten]
; if it returned us null, bail out
test rax, rax
jz .bailout
; otherwise, we can start our loop
; rax == c_text, rdx == c_attr
mov [rsp+48], rax ; save c_text
mov [rsp+56], rdx ; save c_attr
; we need a ton of temporaries here
mov [rsp+64], rbp
mov [rsp+72], rbx
mov [rsp+80], r12
mov [rsp+88], r13
mov [rsp+96], r14
mov [rsp+104], r15
; we'll put those back when we are done
mov rdi, [rsp] ; get our object back, though only temporarily
mov rcx, [rsp+24] ; and the child we are flattening, though only temporarily
; we need to calculate and preserve for our loop:
; clipping rect a.y - our object's bounds.a.y
; clipping rect a.x - our object's bounds.a.x
mov r12d, [rsp+36] ; cliprect.a.y
sub r12d, [rdi+tui_bounds_ay_ofs] ; - our object's bounds.a.y
; scroll mod
sub r12d, [rdi+tui_scroll_ofs+4] ; - our object's scroll.y
; end scroll mod
mov r13d, [rsp+32] ; cliprect.a.x
sub r13d, [rdi+tui_bounds_ax_ofs] ; - our object's bounds.a.x
; scroll mod
sub r13d, [rdi+tui_scroll_ofs] ; - our object's scroll.x
; end scroll mod
; we also need to save the child's width and our object's width
mov r14d, [rdi+tui_width_ofs] ; our object's width
mov r15d, [rcx+tui_width_ofs] ; our child's width
mov r11d, [rsp+44] ; cliprect.b.y
sub r11d, [rsp+36] ; - cliprect.a.y
sub r11d, 1 ; - 1 (we walk this down to zero inclusive and then stop our loop to save a register)
mov r9d, [rsp+40] ; cliprect.b.x
sub r9d, [rsp+32] ; - cliprect.a.x == copy length
mov r8d, [rsp+32] ; cliprect.a.x
sub r8d, [rcx+tui_bounds_ax_ofs] ; - child's bounds.a.x
; scroll mod
add r8d, [rsp+128]
; end scroll mod
mov rdi, [rsp+8] ; get our flat_text buffer
mov rsi, [rsp+16] ; get our flat_attr buffer
; as a precautionary/sanitarium check, make sure line is >= 0
cmp r11d, 0
jl .loopdone
calign
.lineloop:
; first up: calculate our destination offset
mov eax, r11d ; line
add eax, r12d ; + (cliprect.a.y - our object's bounds.a.y)
mul r14d ; * our object's width
add eax, r13d ; + (cliprect.a.x - our object's bounds.a.x)
shl rax, 2 ; in byte offset, not characters
mov rcx, rax ; save the offset
; next up: calculate our source offset
mov eax, r11d ; line
; scroll mod
add eax, [rsp+132]
; end scroll mod
mul r15d ; * our child's width
add eax, r8d ; + (cliprect.a.x - child bounds.a.x)
shl rax, 2 ; in byte offset, not characters
; so now we have:
; ecx == destination offset
; eax == source offset
; r9d == copy length
mov edx, ecx
add rcx, rdi ; flat_text + destination offset
add rdx, rsi ; flat_attr + destination offset
; so now we need source text and source attr pointers (rbp and rbx + source offset)
mov rbp, [rsp+48] ; c_text
mov rbx, [rsp+56] ; c_attr
add rbp, rax
add rbx, rax
; r10d is free, so is rax
xor r10d, r10d ; inner loop/copy offset
calign
.innerloop:
; a note here on why we don't just do a blind memcpy:
; we support "transparent" objects ... which is to say: if the text buffer is null/0
; then we do _not_ flatten said 0, which lets objects be transparent if they want
; this is slower, and surely there is an SSE2 way of makign this go a bit faster
; TODO: someday when I am bored, spend some quality time in this routine
mov eax, dword [rbp+r10*4]
test eax, eax
jz .nocopy
mov dword [rcx+r10*4], eax
mov eax, dword [rbx+r10*4]
mov dword [rdx+r10*4], eax
add r10d, 1
cmp r10d, r9d
jb .innerloop
sub r11d, 1
cmp r11d, 0
jge .lineloop
jmp .loopdone
calign
.bailout:
; it is indeed empty, we are outta here.
add rsp, 136
epilog
calign
.nocopy:
; dword in source text was 0, so we are not copying this spot
add r10d, 1
cmp r10d, r9d
jb .innerloop
sub r11d, 1
cmp r11d, 0
jge .lineloop
calign
.loopdone:
; our copy is complete, free our c_text and c_attr
mov rdi, [rsp+48] ; c_text
call heap$free
mov rdi, [rsp+56] ; c_attr
call heap$free
; so at this point, our next horrendous mess to deal with is if our child
; wants a drop-shadow.
; _we_ have to implement this, because the child itself doesn't have access
; to modify (nor should it) its parent buffer
mov rcx, [rsp+24] ; our child
cmp dword [rcx+tui_dropshadow_ofs], 0
je .cleanupandreturn
; so the actual dropshadow spot is the child bounds moved +1,+1
; so we need a temporary copy of the child bounds, move it +1,+1
; and then intersect it as we did with the original flatten
; and if there is indeed room in the intersected bounds
mov rax, [rcx+tui_bounds_ofs]
mov rdx, [rcx+tui_bounds_ofs+8]
mov [rsp+112], rax
mov [rsp+120], rdx
; now +1,+1 it
add dword [rsp+112], 1
add dword [rsp+116], 1
add dword [rsp+120], 1
add dword [rsp+124], 1 ; childoverdown at rsp+112
; now do an intersection with our object's bounds
mov rdi, [rsp] ; get our object back into rdi
mov rax, [rdi+tui_bounds_ofs]
mov r8, [rdi+tui_bounds_ofs+8]
mov [rsp+32], rax
mov [rsp+40], r8 ; shadowclip at rsp+32
; intersect this temporary bounds object with the child's bounds +1,+1
; rdi == rsp+32
; rsi == [rcx+tui_bounds_ofs]
mov r8d, [rsp+32]
mov r9d, [rsp+40]
mov r10d, [rsp+112]
mov r11d, [rsp+120]
cmp r8d, r10d
cmovl r8d, r10d
cmp r9d, r11d
cmovg r9d, r11d
mov [rsp+32], r8d
mov [rsp+40], r9d
mov r8d, [rsp+36]
mov r9d, [rsp+44]
mov r10d, [rsp+116]
mov r11d, [rsp+124]
cmp r8d, r10d
cmovl r8d, r10d
cmp r9d, r11d
cmovg r9d, r11d
mov [rsp+36], r8d
mov [rsp+44], r9d
; if there was room for our drop shadow, then our shadow cliprect at rsp+32 will contain childoverdown.b
; so....
; we have to see if our intersected bounds.b.x is equal to our moved +1,+1 bounds
; as well as making sure intersected bounds.b.y is equal to our moved +1,+1 bounds
mov rax, [rsp+120] ; b.x and b.y together from the childoverdown rect
cmp rax, [rsp+40] ; compared to our intersected b.x and b.y together
jne .cleanupandreturn ; intersection is kakked
; at this point: we have our shadowclip sitting in rsp+32
; we have our child in rcx, our object in rdi, everything else is free
mov r12d, [rsp+44] ; shadowclip.b.y
sub r12d, [rsp+36] ; - shadowclip.a.y
sub r12d, 1
mov [rsp+112], r12d ; our iteration count for our vertical loop
cmp r12d, 0
jle .shadow_vertdone
mov r12d, [rsp+40] ; shadowclip.b.x
sub r12d, [rsp+32] ; - shadowclip.a.x
sub r12d, 1
shl r12d, 2 ; our column modifier in bytes, first bit
mov [rsp+116], r12d
mov r12d, [rsp+32] ; shadowclip.a.x
sub r12d, [rdi+tui_bounds_ax_ofs] ; - our bounds.a.x
shl r12d, 2
add dword [rsp+116], r12d ; our column modifier in bytes
; next up, figure out our line modifier and width in bytes
mov r12d, [rsp+36] ; shadowclip.a.y
sub r12d, [rdi+tui_bounds_ay_ofs] ; - our bounds.a.y
; leave that sitting in r12d
mov r14d, [rdi+tui_width_ofs]
shl r14d, 2 ; our actual width in bytes
; we are ready for our vertical loop
xor r13d, r13d ; our line
mov rdi, [rsp+8] ; get our flat_text buffer
mov rsi, [rsp+16] ; get our flat_attr buffer
calign
.shadow_vertloop:
mov eax, r13d ; line
add eax, r12d
mul r14d
add eax, dword [rsp+116] ; + our total column modifier
mov r10d, eax
add r10, rsi ; flat_attr + destination offset
mov eax, dword [r10]
test eax, eax
jz .shadow_vertloop_next
; else, darken the colors
push r12 ; darkenindices mashes heaps of regs
call .darkenindices
pop r12
mov dword [r10], eax
calign
.shadow_vertloop_next:
add r13d, 1
sub dword [rsp+112], 1
jnz .shadow_vertloop
calign
.shadow_vertdone:
; next up: do the same for our horizontal side:
mov rdi, [rsp] ; get our object back into rdi
mov rcx, [rsp+24] ; our child
; get our iteration count for our horizontal loop
mov r12d, [rsp+40] ; shadowclip.b.x
sub r12d, [rsp+32] ; - shadowclip.a.x
mov [rsp+112], r12d ; our iteration count for our horizontal loop
cmp r12d, 0
jle .cleanupandreturn
; figure out our basis line modifier
mov r12d, [rsp+44] ; shadowclip.b.y
sub r12d, [rsp+36] ; - shadowclip.a.y
sub r12d, 1
cmp r12d, 0
jle .cleanupandreturn
; and add to that:
mov r13d, [rsp+36] ; shadowclip.a.y
sub r13d, [rdi+tui_bounds_ay_ofs]
add r12d, r13d
; multiply that by our width in bytes, which is still sitting in r14d
mov eax, r12d
mul r14d
; we also need to account for our shadowclip.a.x - our a.x
mov r12d, [rsp+32] ; shadowclip.a.x
sub r12d, [rdi+tui_bounds_ax_ofs] ; - our bounds.a.x
shl r12d, 2
add eax, r12d
mov [rsp+116], eax ; our line modifier
; we are ready for our horizontal loop
xor r13d, r13d ; our column
mov rdi, [rsp+8] ; get our flat_text buffer
mov rsi, [rsp+16] ; get our flat_attr buffer
calign
.shadow_horizloop:
mov eax, r13d ; column
shl eax, 2 ; in bytes
add eax, [rsp+116] ; + line modifier
mov r10d, eax
add r10, rsi ; flat_attr + destination offset
mov eax, dword [r10]
test eax, eax
jz .shadow_horizloop_next
; else, darken the colors
call .darkenindices
mov dword [r10], eax
calign
.shadow_horizloop_next:
add r13d, 1
sub dword [rsp+112], 1
jnz .shadow_horizloop
calign
.cleanupandreturn:
; restore our callee-saves:
mov rbp, [rsp+64]
mov rbx, [rsp+72]
mov r12, [rsp+80]
mov r13, [rsp+88]
mov r14, [rsp+96]
mov r15, [rsp+104]
add rsp, 136
epilog
falign
.darkenindices:
; al/ah need modified
mov r8d, eax
shr r8d, 8
and eax, 0xff
call .darkeneax
mov r9d, eax
mov eax, r8d
call .darkeneax
; put them back together
shl eax, 8
or eax, r9d
ret
falign
.darkeneax:
cmp eax, 0xe8
jae .darken_gray
cmp eax, 16
jb .darken_sys
calign
.darken_doit:
; else, we do it the hard way, TODO: someday when I am bored, come up with a better way than these divs
mov ecx, 6
sub eax, 16
mov r11d, eax ; our index
xor edx, edx
div ecx ; div 6
mov r12d, edx ; blue == remainder
mov r11d, eax
xor edx, edx
div ecx
mov r15d, edx ; green == remainder
mov r11d, eax
xor edx, edx
div ecx
; red now sitting in edx
mov eax, edx
sub eax, 1
cmp edx, 0
cmova edx, eax
mov eax, r15d
sub eax, 1
cmp r15d, 0
cmova r15d, eax
mov eax, r12d
sub eax, 1
cmp r12d, 0
cmova r12d, eax
mov ecx, 36
; our return is r12d + (r15d * 6) + (edx * 36) + 16
mov eax, edx
mul ecx
add r12d, eax
mov ecx, 6
add r12d, 16
mov eax, r15d
mul ecx
add r12d, eax
xchg eax, r12d
ret
calign
.darken_gray:
sub eax, 0xe8
shr eax, 1
add eax, 0xe8
ret
calign
.darken_sys:
jmp qword [rax*8+.darken_dispatch]
dalign
.darken_dispatch:
dq .d0, .d1, .d2, .d3, .d4, .d5, .d6, .d7, .d8, .d9, .d10, .d11, .d12, .d13, .d14, .d15
calign
.d0:
; eax is already zero, return 0
ret
calign
.d1:
ansi_rgbi eax, 194, 54, 33
jmp .darken_doit
calign
.d2:
ansi_rgbi eax, 37, 188, 36
jmp .darken_doit
calign
.d3:
ansi_rgbi eax, 173, 173, 39
jmp .darken_doit
calign
.d4:
ansi_rgbi eax, 73, 46, 225
jmp .darken_doit
calign
.d5:
ansi_rgbi eax, 211, 56, 211
jmp .darken_doit
calign
.d6:
ansi_rgbi eax, 51, 187, 200
jmp .darken_doit
calign
.d7:
ansi_rgbi eax, 203, 204, 205
jmp .darken_doit
calign
.d8:
ansi_rgbi eax, 129, 131, 131
jmp .darken_doit
calign
.d9:
ansi_rgbi eax, 252, 57, 31
jmp .darken_doit
calign
.d10:
ansi_rgbi eax, 49, 231, 34
jmp .darken_doit
calign
.d11:
ansi_rgbi eax, 234, 236, 35
jmp .darken_doit
calign
.d12:
ansi_rgbi eax, 88, 51, 255
jmp .darken_doit
calign
.d13:
ansi_rgbi eax, 249, 53, 248
jmp .darken_doit
calign
.d14:
ansi_rgbi eax, 20, 240, 240
jmp .darken_doit
calign
.d15:
mov eax, 0xe8 + 12
ret
end if
if used tui_object$flatten | defined include_everything
; single argument: rdi == our tui_object
; returns newly allocated flat_text buffer in rax, flat_attr buffer in rdx
; NOTE: due to second return in rdx, not sure how a C/C++ wrapper for this would work
; but it would be trivial to create a second version that accepts pointers to buffers
; like the old version does
falign
tui_object$flatten:
prolog tui_object$flatten
cmp qword [rdi+tui_text_ofs], 0
je .nullret
mov eax, [rdi+tui_width_ofs]
mov ecx, [rdi+tui_height_ofs]
mul ecx
shl eax, 2
test eax, eax
jz .nullret
sub rsp, 32
mov [rsp], rdi
mov dword [rsp+24], eax
mov edi, eax
call heap$alloc
mov [rsp+8], rax
mov edi, dword [rsp+24]
call heap$alloc
mov [rsp+16], rax
; copy our text into our new flat_text, and our attr into our new flat_attr
mov rcx, [rsp]
mov rdi, [rsp+8]
mov rsi, [rcx+tui_text_ofs]
mov edx, dword [rsp+24]
call memcpy
mov rcx, [rsp]
mov rdi, [rsp+16]
mov rsi, [rcx+tui_attr_ofs]
mov edx, dword [rsp+24]
call memcpy
; so now we have a copy of our own buffer, now it is time to iterate over our children
; and if they are visible and have valid bounds, stick their flattened goods into our
; new buffers at the right spot
mov rcx, [rsp]
mov rdi, [rcx+tui_children_ofs]
mov r8, [_list_first]
test r8, r8
jz .flattenbastards
calign
.loop:
mov rcx, [r8+_list_valueofs] ; the actual child object
cmp dword [rcx+tui_visible_ofs], 0
je .next
cmp qword [rcx+tui_text_ofs], 0
je .next
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov [rsp+24], r8 ; save our pointer so we can keep going
call tui_object$nvflattenchild
mov r8, [rsp+24]
calign
.next:
mov r8, [r8+_list_nextofs]
test r8, r8
jnz .loop
calign
.flattenbastards:
mov rcx, [rsp]
mov rdi, [rcx+tui_bastards_ofs]
mov r8, [_list_first]
test r8, r8
jz .alldone
calign
.bloop:
mov rcx, [r8+_list_valueofs] ; the actual child object
cmp dword [rcx+tui_visible_ofs], 0
je .bnext
cmp qword [rcx+tui_text_ofs], 0
je .bnext
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov [rsp+24], r8 ; save our pointer so we can keep going
call tui_object$nvflattenchild
mov r8, [rsp+24]
calign
.bnext:
mov r8, [r8+_list_nextofs]
test r8, r8
jnz .bloop
calign
.alldone:
mov rax, [rsp+8]
mov rdx, [rsp+16]
add rsp, 32
epilog
calign
.nullret:
xor eax, eax
xor edx, edx
epilog
end if
if used tui_object$firekeyevent | defined include_everything
; three arguments: rdi == our tui_object, esi == key, edx == esc_key
; this fires off the key events to all children (visible or not)
; stops when one of them says so via its return
; returns zero in eax if no one did, or 1 if so
falign
tui_object$firekeyevent:
prolog tui_object$firekeyevent
sub rsp, 32
mov [rsp], rdi
mov [rsp+8], rsi
mov [rsp+16], rdx
cmp qword [rdi+tui_focus_ofs], 0
jne .usefocus
; hmmm, do we pass it to our children before we check ourselves?
; TODO: come back and think more about this
mov rdi, [rdi+tui_children_ofs]
mov rcx, [_list_first]
test rcx, rcx
jz .nokids
calign
.loop:
mov [rsp+24], rcx
mov rdi, [rcx+_list_valueofs]
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov rcx, [rdi]
call qword [rcx+tui_vfirekeyevent]
test eax, eax
jnz .kidsdone
mov rcx, [rsp+24]
mov rcx, [rcx+_list_nextofs]
test rcx, rcx
jnz .loop
calign
.nokids:
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov rcx, [rdi] ; get our vtable
call qword [rcx+tui_vkeyevent]
add rsp, 32
epilog
calign
.usefocus:
mov rdi, [rdi+tui_focus_ofs]
mov rcx, [rdi] ; get its vtable
call qword [rcx+tui_vfirekeyevent]
test eax, eax
jnz .usefocusdone
mov rdi, [rsp]
mov rsi, [rsp+8]
mov rdx, [rsp+16]
mov rcx, [rdi] ; get our vtable
call qword [rcx+tui_vkeyevent]
add rsp, 32
epilog
calign
.usefocusdone:
.kidsdone:
add rsp, 32
epilog
end if
if used tui_object$ontab | defined include_everything
; single argument: our tui object in rdi
; default propagates the call up the tree
falign
tui_object$ontab:
prolog tui_object$ontab
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi] ; load up the parent's vtable
call qword [rsi+tui_vontab]
epilog
calign
.noparent:
epilog
end if
if used tui_object$onshifttab | defined include_everything
; single argument: our tui object in rdi
; default propagates the call up the tree
falign
tui_object$onshifttab:
prolog tui_object$onshifttab
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rsi, [rdi] ; load up the parent's vtable
call qword [rsi+tui_vonshifttab]
epilog
calign
.noparent:
epilog
end if
if used tui_object$click | defined include_everything
; single argument: our tui object in rdi
; default calls parent's clicked vmethod with our rdi as its argument
falign
tui_object$click:
prolog tui_object$click
mov rsi, rdi
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rdx, [rdi] ; load up the parent's vtable
call qword [rdx+tui_vclicked]
epilog
calign
.noparent:
epilog
end if
if used tui_object$clicked | defined include_everything
; two arguments: rdi == our tui_object, rsi == displayobject that fired the click
; default just walks up the tree
falign
tui_object$clicked:
prolog tui_object$clicked
mov rdi, [rdi+tui_parent_ofs]
test rdi, rdi
jz .noparent
mov rdx, [rdi] ; load up the parent's vtable
call qword [rdx+tui_vclicked]
epilog
calign
.noparent:
epilog
end if
if used tui_object$nvbox | defined include_everything
; six arguments: tui_object (or descendent) in rdi, x in esi, y in edx, width in ecx, height in r8d, colors in r9d
; this is a convenience function to draw a box, since lots of descendent components do such things
falign
tui_object$nvbox:
prolog tui_object$nvbox
mov eax, [rdi+tui_width_ofs] ; actual object width
mov r10, [rdi+tui_text_ofs]
mov r11, [rdi+tui_attr_ofs]
test eax, eax
jz .bailout
test r10, r10
jz .bailout
cmp esi, dword [rdi+tui_width_ofs]
jae .bailout ; sanity check
cmp edx, dword [rdi+tui_height_ofs]
jae .bailout ; sanity check
sub eax, esi ; actual object width - x == max width
cmp ecx, eax
cmova ecx, eax
mov eax, [rdi+tui_height_ofs]
sub eax, edx ; actual object height - y == max height
cmp r8d, eax
cmova r8d, eax
cmp ecx, 0
jle .bailout
cmp r8d, 0
jle .bailout
; values sanitized, we need to make use of a few more temporaries
push rbx r12 r13 r14 r15
; being a bit lazy here, hahah
mov eax, [rdi+tui_width_ofs]
mul edx ; y * actual object width
shl eax, 2 ; in bytes
mov ebx, esi ; x
shl ebx, 2 ; in bytes
mov r12d, [rdi+tui_width_ofs] ; actual object width
shl r12d, 2 ; in bytes
; update both r10 and r11 by our starting location
add r10, rax
add r11, rax
; do the first line
mov r14, r10
mov r15, r11
add r14, rbx
add r15, rbx
mov r13d, ecx ; our width in characters
sub r13d, 2 ; less corners
mov dword [r14], 0x250c ; ULCORNER
mov dword [r15], r9d ; attr
add r14, 4
add r15, 4
cmp r13d, 0
jle .firstlinenomiddle
calign
.firstlineloop:
mov dword [r14], 0x2500 ; HLINE
mov dword [r15], r9d ; attr
add r14, 4
add r15, 4
sub r13d, 1
jnz .firstlineloop
calign
.firstlinenomiddle:
mov dword [r14], 0x2510 ; URCORNER
mov dword [r15], r9d ; attr
add r14, 4
add r15, 4
; now we can increment r10 and r11 by a whole line
add r10, r12
add r11, r12
; now r10 and r11 are sitting on the second line left edge of actual object (not x-modified)
mov r13d, r8d ; height
sub r13d, 2 ; -2 cuz we are doing top/bottom separately
cmp r13d, 0
jle .nomiddle
mov edx, ecx ; our box width
shl edx, 2 ; in bytes
sub edx, 4 ; offset to last position
calign
.midloop:
mov r14, r10
mov r15, r11
add r14, rbx
add r15, rbx
mov dword [r14], 0x2502 ; VLINE
mov dword [r15], r9d ; attr
add r14, rdx
add r15, rdx
mov dword [r14], 0x2502 ; VLINE
mov dword [r15], r9d ; attr
add r10, r12
add r11, r12
sub r13d, 1
jnz .midloop
calign
.nomiddle:
mov r14, r10
mov r15, r11
add r14, rbx
add r15, rbx
mov r13d, ecx
sub r13d, 2
mov dword [r14], 0x2514 ; LLCORNER
mov dword [r15], r9d
add r14, 4
add r15, 4
cmp r13d, 0
jle .lastlinenomiddle
calign
.lastlineloop:
mov dword [r14], 0x2500 ; HLINE
mov dword [r15], r9d
add r14, 4
add r15, 4
sub r13d, 1
jnz .lastlineloop
calign
.lastlinenomiddle:
mov dword [r14], 0x2518 ; LRCORNER
mov dword [r15], r9d
pop r15 r14 r13 r12 rbx
epilog
calign
.bailout:
epilog
end if
if used tui_object$nvtile | defined include_everything
; two arguments: rdi == a tui_object or descendent, esi == bool for horizontal or not
; CAUTION: this forces the layout to absolute for the object in rdi (as it must)
; further: if we don't have valid bounds, this doesn't do much
falign
tui_object$nvtile:
prolog tui_object$nvtile
mov rdx, [rdi+tui_children_ofs]
mov rcx, [rdx+_list_size_ofs]
test ecx, ecx
jz .nothingtodo
mov dword [rdi+tui_layout_ofs], tui_layout_absolute
; figure out the most-equal divisors for our child count in ecx
; we'll need the integer square root of ecx first up
mov edx, ecx
xor r8d, r8d
mov r9d, 1 shl 30
; make soure our bounds is not empty
mov r10d, [rdi+tui_bounds_ax_ofs]
mov r11d, [rdi+tui_bounds_ay_ofs]
cmp r10d, [rdi+tui_bounds_bx_ofs]
jl .isqr1
cmp r11d, [rdi+tui_bounds_by_ofs]
jae .nothingtodo
calign
.isqr1:
cmp r9d, edx
jbe .isqr2
shr r9d, 2
jmp .isqr1
calign
.isqr2:
mov eax, r8d
test r9d, r9d
jz .isqr2_done
add eax, r9d
cmp edx, eax
jae .isqr2_mod
shr r8d, 1
shr r9d, 2
jmp .isqr2
calign
.isqr2_mod:
mov r10d, r9d
sub edx, eax
shl r10d, 1
add r8d, r10d
shr r8d, 1
shr r9d, 2
jmp .isqr2
calign
.isqr2_done:
; result is sitting in r8d
xor edx, edx
mov eax, ecx
div r8d
test edx, edx
jz .med_check2
mov r10d, r8d
xor edx, edx
add r10d, 1
mov eax, ecx
div r10d
test edx, edx
cmovz r8d, r10d
calign
.med_check2:
xor edx, edx
mov eax, ecx
div r8d
cmp r8d, eax
cmovb r8d, eax
xor edx, edx
mov eax, ecx
div r8d
; x == eax
; y == r8d
; childcount still sitting in ecx
test esi, esi
cmovnz r10d, eax
cmovnz r11d, r8d
cmovz r10d, r8d
cmovz r11d, eax
; number of columns now sitting in r10d
; number of rows now sitting in r11d
; make sure our bounds rect will allow our cols/rows to fit
mov eax, [rdi+tui_bounds_bx_ofs]
xor edx, edx
sub eax, [rdi+tui_bounds_ax_ofs]
div r10d
test eax, eax
jz .nothingtodo ; tiling error if width / number of columns == 0
mov eax, [rdi+tui_bounds_by_ofs]
xor edx, edx
sub eax, [rdi+tui_bounds_ay_ofs]
div r11d
test eax, eax
jz .nothingtodo ; tiling error if height / number of rows == 0
; otherwise, we need some more regs
push rbx r12 r13 r14 r15
; figure out how many are leftover
mov eax, ecx
xor edx, edx
div r10d
mov r14d, ecx
mov r15d, edx ; leftover count in r15d
sub r14d, 1 ; starting tile number in r14d
; next up, walk through our children list one by one
mov r13, [rdi+tui_children_ofs]
mov r13, [r13+_list_first_ofs]
calign
.childloop:
mov r12, [r13] ; the actual tui_object child at this list position
; save a copy of the child's width/height before we do anything, to see if we need
; to call its sizechanged method or not
mov eax, [r12+tui_width_ofs]
mov edx, [r12+tui_height_ofs]
push rax rdx
; next up, we need to calculate the tile rect for this child
; x in r8d
; y in r9d
; d in ecx
mov eax, r10d ; number of columns
sub eax, r15d ; less how many are leftover
mul r11d ; * number of rows
mov ecx, eax
cmp r14d, ecx
jae .childloop_tilerect_case2
; else, childloop_tilerect_case1
; set x = tile number / number of rows
xor edx, edx
mov eax, r14d
div r11d
mov r8d, eax
; set y = tile number % number of rows
mov r9d, edx
; now calculate the actual child bounds, we are done with ecx
; child bounds:
; a.x = (((parent.b.x - parent.a.x) * x) / numcols) + parent.a.x
mov ecx, r8d
mov eax, [rdi+tui_bounds_bx_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
mul r8d
div r10d
add eax, [rdi+tui_bounds_ax_ofs]
mov [r12+tui_bounds_ax_ofs], eax
add ecx, 1
; b.x = (((parent.b.x - parent.a.x) * (x+1) / numcols) + parent.a.x
mov eax, [rdi+tui_bounds_bx_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
mul ecx
div r10d
add eax, [rdi+tui_bounds_ax_ofs]
mov [r12+tui_bounds_bx_ofs], eax
; a.y = (((parent.b.y - parent.a.y) * y) / numrows) + parent.a.y
mov ecx, r9d
mov eax, [rdi+tui_bounds_by_ofs]
sub eax, [rdi+tui_bounds_ay_ofs]
mul r9d
div r11d
add eax, [rdi+tui_bounds_ay_ofs]
mov [r12+tui_bounds_ay_ofs], eax
add ecx, 1
; b.y = (((parent.b.y - parent.a.y) * (y+1) / numrows) + parent.a.y
mov eax, [rdi+tui_bounds_by_ofs]
sub eax, [rdi+tui_bounds_ay_ofs]
mul ecx
div r11d
add eax, [rdi+tui_bounds_ay_ofs]
mov [r12+tui_bounds_by_ofs], eax
jmp .childloop_tilerect_done
calign
.childloop_tilerect_case2:
mov eax, r14d
mov r9d, r11d
sub eax, ecx
add r9d, 1
xor edx, edx
div r9d
; set y = tilenumber - d % (number of rows + 1)
mov r9d, edx
; set x = tilenumber - d / (number of rows + 1) + (number of columns - leftover count)
mov r8d, r10d
sub r8d, r15d
add eax, r8d
xchg eax, r8d
; now calculate the actual child bounds, we are done with ecx
; child bounds:
; a.x = (((parent.b.x - parent.a.x) * x) / numcols) + parent.a.x
mov ecx, r8d
mov eax, [rdi+tui_bounds_bx_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
mul r8d
div r10d
add eax, [rdi+tui_bounds_ax_ofs]
mov [r12+tui_bounds_ax_ofs], eax
add ecx, 1
; b.x = (((parent.b.x - parent.a.x) * (x+1) / numcols) + parent.a.x
mov eax, [rdi+tui_bounds_bx_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
mul ecx
div r10d
add eax, [rdi+tui_bounds_ax_ofs]
mov [r12+tui_bounds_bx_ofs], eax
; temporarily increase number of rows by 1
add r11d, 1
; a.y = (((parent.b.y - parent.a.y) * y) / (numrows+1)) + parent.a.y
mov ecx, r9d
mov eax, [rdi+tui_bounds_by_ofs]
sub eax, [rdi+tui_bounds_ay_ofs]
mul r9d
div r11d
add eax, [rdi+tui_bounds_ay_ofs]
mov [r12+tui_bounds_ay_ofs], eax
add ecx, 1
; b.y = (((parent.b.y - parent.a.y) * (y+1) / (numrows+1)) + parent.a.y
mov eax, [rdi+tui_bounds_by_ofs]
sub eax, [rdi+tui_bounds_ay_ofs]
mul ecx
div r11d
add eax, [rdi+tui_bounds_ay_ofs]
mov [r12+tui_bounds_by_ofs], eax
; restore our number of rows
sub r11d, 1
calign
.childloop_tilerect_done:
; set its width, height, absolutex and absolutey
mov eax, [r12+tui_bounds_bx_ofs]
mov ecx, [r12+tui_bounds_by_ofs]
sub eax, [r12+tui_bounds_ax_ofs]
sub ecx, [r12+tui_bounds_ay_ofs]
mov [r12+tui_width_ofs], eax
mov [r12+tui_height_ofs], ecx
mov eax, [r12+tui_bounds_ax_ofs]
mov ecx, [r12+tui_bounds_ay_ofs]
sub eax, [rdi+tui_bounds_ax_ofs]
sub ecx, [rdi+tui_bounds_ay_ofs]
mov [r12+tui_absolutex_ofs], eax
mov [r12+tui_absolutey_ofs], ecx
pop rdx rax
cmp eax, [r12+tui_width_ofs]
jne .childloop_sizechanged
cmp edx, [r12+tui_height_ofs]
jne .childloop_sizechanged
sub r14d, 1
mov r13, [r13+_list_nextofs]
test r13, r13
jnz .childloop
pop r15 r14 r13 r12 rbx
mov rsi, [rdi]
call qword [rsi+tui_vlayoutchanged]
epilog
calign
.childloop_sizechanged:
; we need to save r10d, r11d, rdi
push rdi r10 r11
mov rdi, r12
mov rsi, [r12]
call qword [rsi+tui_vsizechanged]
pop r11 r10 rdi
sub r14d, 1
mov r13, [r13+_list_nextofs]
test r13, r13
jnz .childloop
pop r15 r14 r13 r12 rbx
mov rsi, [rdi]
call qword [rsi+tui_vlayoutchanged]
epilog
calign
.nothingtodo:
epilog
end if
if used tui_object$nvcascade | defined include_everything
; single argument in rdi: our tui_object
; CAUTION: this forces the layout to absolute for the object in rdi (as it must)
;
; since we don't do preferred dimensions on objects, if you tile and then cascade
; the windows won't get their original dims back of course...
falign
tui_object$nvcascade:
prolog tui_object$nvcascade
mov rdx, [rdi+tui_children_ofs]
mov rcx, [rdx+_list_size_ofs]
test ecx, ecx
jz .nothingtodo
mov dword [rdi+tui_layout_ofs], tui_layout_absolute
mov rcx, [rdx+_list_first_ofs]
xor eax, eax
calign
.childloop:
mov rdx, [rcx] ; the actual child object
mov [rdx+tui_absolutex_ofs], eax
mov [rdx+tui_absolutey_ofs], eax
add eax, 1
mov rcx, [rcx+_list_nextofs]
test rcx, rcx
jnz .childloop
mov rsi, [rdi]
call qword [rsi+tui_vlayoutchanged]
epilog
calign
.nothingtodo:
epilog
end if