HeavyThing - tui_label.inc

Jeff Marrison

Table of functions

	; ------------------------------------------------------------------------
	; 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_label.inc: text displaying goods
	; ... basically a background with layout-aware text goodies, string based
	; ... deals with multiline, but is not particularly smart about it, nor editable
	;
	; if you want heavier text layout/editing/etc, see tui_text.inc
	;
	; uses list and string of course in addition to tui_background, tui_object
	;

	; NOTE: this requires string32 not string16 (makes things simpler due to our dd buffer sizing)
	; TODO: someday when I am bored, redo this to support UTF16 strings instead of 32bit
	;

if used tui_label$vtable | defined include_everything

if string_bits = 16
	display 'ERROR: tui_label requires strings_bits = 32 in settings'
	err
end if

dalign
tui_label$vtable:
        dq      tui_label$cleanup, tui_label$clone, tui_label$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

tui_textalign_left = 0
tui_textalign_center = 1
tui_textalign_right = 2
tui_textalign_justified = 3				; not supported in tui_label, see tui_text if you want fancier goods

tui_label_filltext_ofs = tui_background_size
tui_label_textalign_ofs = tui_background_size + 8		; dd
tui_label_lines_ofs = tui_background_size + 16		; list of string
tui_label_highlightchar_ofs = tui_background_size + 24	; dd
tui_label_highlightcolor_ofs = tui_background_size + 32	; dd

tui_label_size = tui_background_size + 40


	; since this is rarely if-ever descended, we provide $new constructors
	; instead of $init based ones like tui_object

	; use tui_label$clone if you want a copy


	; NOTE: we do not assume ownership of filltext strings passed to us at new
	; we make a copy of it...

if used tui_label$new_ii | defined include_everything
	; five arguments: edi == width, esi == height, rdx == filltext (string), ecx == colors, r8d == text alignment
falign
tui_label$new_ii:
	prolog	tui_label$new_ii
	sub	rsp, 56
	mov	[rsp], rdi
	mov	[rsp+8], rsi
	mov	[rsp+16], rdx
	mov	[rsp+24], rcx
	mov	[rsp+32], r8
	mov	rdi, rdx
	call	string$copy
	mov	[rsp+16], rax		; copy of our string
	call	list$new
	mov	[rsp+40], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+48], rax
	mov	rdi, rax
	mov	rsi, [rsp]		; width
	mov	rdx, [rsp+8]		; height
	mov	ecx, ' '		; fillchar
	mov	r8, [rsp+24]		; fillcolors
	call	tui_background$init_ii
	mov	rdi, [rsp+48]
	mov	rsi, [rsp+16]		; our filltext string
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	rsi, [rsp+40]		; first list
	mov	[rdi+tui_label_lines_ofs], rsi
	mov	rsi, [rsp+32]
	mov	[rdi+tui_label_textalign_ofs], rsi
	mov	qword [rdi+tui_label_highlightchar_ofs], 0
	mov	qword [rdi+tui_label_highlightcolor_ofs], 0
	call	tui_label$nvlineate
	mov	rax, [rsp+48]
	add	rsp, 56
	epilog
end if


if used tui_label$new_str | defined include_everything
	; three arguments: rdi == filltext (string), esi == colors, edx == text alignment
	; NOTE: this calculates teh required dimensions for the string and then passes it to new_ii
falign
tui_label$new_str:
	prolog	tui_label$new_str
	push	rdi rsi rdx
	mov	esi, 10
	call	string$split
	sub	rsp, 16
	mov	[rsp], rax
	mov	qword [rsp+8], 0
	mov	rdi, rax
	mov	rsi, .linelength
	lea	rdx, [rsp+8]
	call	list$foreach_arg
	; dword [rsp+8] == max line length
	; dword [rsp+12] == line count
	mov	rdi, [rsp]
	mov	rsi, heap$free
	call	list$clear
	mov	rdi, [rsp]
	call	heap$free
	mov	edi, [rsp+8]
	mov	esi, [rsp+12]
	add	rsp, 16
	pop	r8 rcx rdx
	call	tui_label$new_ii
	epilog
falign
.linelength:
	; rdi == string of this line, rsi == pointer that we need to udpate
	mov	eax, [rdi]
	mov	edx, [rsi]
	add	dword [rsi+4], 1
	cmp	eax, edx
	cmova	edx, eax
	mov	[rsi], edx
	ret

end if


if used tui_label$new_dd | defined include_everything
	; five arguments: xmm0 == width percent, xmm1 == height percent, rdi == filltext (string), esi == colors, edx == text alignment
falign
tui_label$new_dd:
	prolog	tui_label$new_dd
	sub	rsp, 56
	movq	[rsp], xmm0
	movq	[rsp+8], xmm1
	mov	[rsp+16], rdi
	mov	[rsp+24], rsi
	mov	[rsp+32], rdx
	call	string$copy
	mov	[rsp+16], rax		; copy of our string
	call	list$new
	mov	[rsp+40], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+48], rax
	mov	rdi, rax
	movq	xmm0, [rsp]		; width percent
	movq	xmm1, [rsp+8]		; height percent
	mov	esi, ' '		; fillchar
	mov	rdx, [rsp+24]		; fillcolors
	call	tui_background$init_dd
	mov	rdi, [rsp+48]
	mov	rsi, [rsp+16]		; our filltext string
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	rsi, [rsp+40]		; first list
	mov	[rdi+tui_label_lines_ofs], rsi
	mov	rsi, [rsp+32]
	mov	[rdi+tui_label_textalign_ofs], rsi
	mov	qword [rdi+tui_label_highlightchar_ofs], 0
	mov	qword [rdi+tui_label_highlightcolor_ofs], 0
	call	tui_label$nvlineate
	mov	rax, [rsp+48]
	add	rsp, 56
	epilog
end if

if used tui_label$new_id | defined include_everything
	; five arguments: edi == width, xmm0 == height percent, rsi == filltext (string), edx == colors, ecx == text alignment
falign
tui_label$new_id:
	prolog	tui_label$new_id
	sub	rsp, 56
	mov	[rsp], rdi
	movq	[rsp+8], xmm0
	mov	[rsp+16], rsi
	mov	[rsp+24], rdx
	mov	[rsp+32], rcx
	mov	rdi, rsi
	call	string$copy
	mov	[rsp+16], rax		; copy of our string
	call	list$new
	mov	[rsp+40], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+48], rax
	mov	rdi, rax
	mov	rsi, [rsp]		; width
	movq	xmm0, [rsp+8]		; height percent
	mov	edx, ' '		; fillchar
	mov	rcx, [rsp+24]
	call	tui_background$init_id
	mov	rdi, [rsp+48]
	mov	rsi, [rsp+16]		; our filltext string
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	rsi, [rsp+40]		; first list
	mov	[rdi+tui_label_lines_ofs], rsi
	mov	rsi, [rsp+32]
	mov	[rdi+tui_label_textalign_ofs], rsi
	mov	qword [rdi+tui_label_highlightchar_ofs], 0
	mov	qword [rdi+tui_label_highlightcolor_ofs], 0
	call	tui_label$nvlineate
	mov	rax, [rsp+48]
	add	rsp, 56
	epilog
end if

if used tui_label$new_di | defined include_everything
	; five arguments: xmm0 == width percent, edi == height, rsi == filltext (string), edx == colors, ecx == text alignment
falign
tui_label$new_di:
	prolog	tui_label$new_di
	sub	rsp, 56
	movq	[rsp], xmm0
	mov	[rsp+8], rdi
	mov	[rsp+16], rsi
	mov	[rsp+24], rdx
	mov	[rsp+32], rcx
	mov	rdi, rsi
	call	string$copy
	mov	[rsp+16], rax		; copy of our string
	call	list$new
	mov	[rsp+40], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+48], rax
	mov	rdi, rax
	movq	xmm0, [rsp]		; width percent
	mov	rsi, [rsp+8]		; height
	mov	edx, ' '		; fillchar
	mov	rcx, [rsp+24]
	call	tui_background$init_di
	mov	rdi, [rsp+48]
	mov	rsi, [rsp+16]		; our filltext string
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	rsi, [rsp+40]		; first list
	mov	[rdi+tui_label_lines_ofs], rsi
	mov	rsi, [rsp+32]
	mov	[rdi+tui_label_textalign_ofs], rsi
	mov	qword [rdi+tui_label_highlightchar_ofs], 0
	mov	qword [rdi+tui_label_highlightcolor_ofs], 0
	call	tui_label$nvlineate
	mov	rax, [rsp+48]
	add	rsp, 56
	epilog
end if

if used tui_label$new_rect | defined include_everything
	; four arguments: rdi == pointer to a bounds rect, rsi == filltext (string), edx == colors, ecx == text alignment
falign
tui_label$new_rect:
	prolog	tui_label$new_rect
	sub	rsp, 48
	mov	[rsp], rdi
	mov	[rsp+8], rsi
	mov	[rsp+16], rdx
	mov	[rsp+24], rcx
	mov	rdi, rsi
	call	string$copy
	mov	[rsp+8], rax
	call	list$new
	mov	[rsp+32], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+40], rax
	mov	rdi, rax
	mov	rsi, [rsp]		; rect
	mov	edx, ' '		; fillchar
	mov	rcx, [rsp+16]		; fillcolors
	call	tui_background$init_rect
	mov	rdi, [rsp+40]
	mov	rsi, [rsp+8]		; our filltext string
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	rsi, [rsp+32]		; first list
	mov	[rdi+tui_label_lines_ofs], rsi
	mov	rsi, [rsp+24]
	mov	[rdi+tui_label_textalign_ofs], rsi
	mov	qword [rdi+tui_label_highlightchar_ofs], 0
	mov	qword [rdi+tui_label_highlightcolor_ofs], 0
	call	tui_label$nvlineate
	mov	rax, [rsp+40]
	add	rsp, 48
	epilog
end if


if used tui_label$clone | defined include_everything
	; single argument: rdi == source tui_label to clone/make a copy of
	; returns new tui_label copy in rax
falign
tui_label$clone:
	prolog	tui_label$clone
	sub	rsp, 32
	mov	[rsp], rdi
	mov	rdi, [rdi+tui_label_filltext_ofs]	; source string
	call	string$copy
	mov	[rsp+8], rax
	call	list$new
	mov	[rsp+16], rax
	mov	edi, tui_label_size
	call	heap$alloc
	mov	qword [rax], tui_label$vtable
	mov	[rsp+24], rax
	mov	rdi, rax
	mov	rsi, [rsp]
	call	tui_background$init_copy
	mov	rdi, [rsp+24]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	mov	[rdi+tui_label_filltext_ofs], rsi
	mov	[rdi+tui_label_lines_ofs], rdx
	mov	rsi, [rsp]
	mov	rdx, [rsi+tui_label_textalign_ofs]
	mov	[rdi+tui_label_textalign_ofs], rdx
	mov	r8, [rsi+tui_label_highlightchar_ofs]
	mov	r9, [rsi+tui_label_highlightcolor_ofs]
	mov	[rdi+tui_label_highlightchar_ofs], r8
	mov	[rdi+tui_label_highlightcolor_ofs], r9
	call	tui_label$nvlineate
	mov	rax, [rsp+24]
	add	rsp, 32
	epilog
end if

if used tui_label$nvlineate | defined include_everything
	; single argument: rdi == tui_label object, this is non-virtual (hence nv preface)
	; we split the filltext by linefeeds and populate our textlines list
falign
tui_label$nvlineate:
	prolog	tui_label$nvlineate
	push	rdi
	; first, make sure our textlines list is empty:
	mov	rdi, [rdi+tui_label_lines_ofs]
	mov	rsi, heap$free
	call	list$clear
	mov	rsi, [rsp]
	mov	rdi, [rsi+tui_label_lines_ofs]
	call	heap$free		; blast the old one entirely
	mov	rdx, [rsp]		; get our tui_label object back
	mov	rdi, [rdx+tui_label_filltext_ofs]
	mov	esi, 10			; LF
	call	string$split
	mov	rdi, [rsp]
	mov	rcx, [rax+_list_size_ofs]
	mov	[rdi+tui_label_lines_ofs], rax
	pop	rdi
	epilog
calign
.dumpit:
	call	string$to_stdoutln
	ret
end if

if used tui_label$cleanup | defined include_everything
	; single argument: rdi == tui_label we are destroying
	; NOTE: this does not free the pointer itself, but heap$free's everything that we allocated
	; and iterates/deletes its children/bastards just like tui_object does
falign
tui_label$cleanup:
	prolog	tui_label$cleanup
	push	rdi
	mov	rdi, [rdi+tui_label_lines_ofs]
	mov	rsi, heap$free
	call	list$clear
	mov	rsi, [rsp]
	mov	rdi, [rsi+tui_label_lines_ofs]
	call	heap$free
	mov	rdx, [rsp]
	mov	rdi, [rdx+tui_label_filltext_ofs]
	call	heap$free
	
	; background's cleanup doesn't free anything, so we can safely just let tui_object do the rest
	mov	rdi, [rsp]
	call	tui_object$cleanup

	pop	rdi
	epilog
end if


if used tui_label$nvsettext | defined include_everything
	; two arguments: rdi == tui_label, rsi == new string of text to set
	; note: we make a copy of rsi
falign
tui_label$nvsettext:
	prolog	tui_label$nvsettext
	push	rsi rdi
	; first up, get rid of our previous one
	mov	rdi, [rdi+tui_label_filltext_ofs]
	call	heap$free
	mov	rdi, [rsp+8]	; our string
	call	string$copy
	mov	rdi, [rsp]
	mov	[rdi+tui_label_filltext_ofs], rax	; store our new copy
	call	tui_label$nvlineate
	mov	rdi, [rsp]
	; this one is a virtual
	mov	rsi, [rdi]	; our vtable
	call	qword [rsi+tui_vdraw]
	pop	rdi rsi
	epilog
end if

if used tui_label$nvsetalign | defined include_everything
	; two arguments: rdi == tui_label, esi == new alignment type
falign
tui_label$nvsetalign:
	prolog	tui_label$nvsetalign
	mov	rdx, [rdi]	; our vtable
	mov	dword [rdi+tui_label_textalign_ofs], esi
	call	qword [rdx+tui_vdraw]
	epilog
end if

if used tui_label$nvsetcolors | defined include_everything
	; two arguments: rdi == tui_label, esi == new colors
falign
tui_label$nvsetcolors:
	prolog	tui_label$nvsetcolors
	mov	rdx, [rdi]	; our vtable
	mov	dword [rdi+tui_bgcolors_ofs], esi
	call	qword [rdx+tui_vdraw]
	epilog
end if

if used tui_label$nvaddline | defined include_everything
	; two arguments: rdi == tui_label, rsi == new string of line to add
	; NOTE: this is a "cheater" method, insofar as: we do not modify filltext itself
	; but instead just add an entry to the textlines list, which is all that is used for drawing anyway
	; STRING PASSED IN MUST NOT CONTAIN a linefeed
falign
tui_label$nvaddline:
	prolog	tui_label$nvaddline
	push	rdi
	mov	rdi, rsi
	call	string$copy
	mov	rdx, [rsp]
	mov	rdi, [rdx+tui_label_lines_ofs]
	mov	rsi, rax
	call	list$push_back
	mov	rdi, [rsp]
	mov	rsi, [rdi]
	call	qword [rsi+tui_vdraw]
	pop	rdi
	epilog
end if
	

if used tui_label$draw | defined include_everything
	; single argument: rdi == tui_label
	; renders/draws our text, then calls the virtual updatedisplaylist method
falign
tui_label$draw:
	prolog	tui_label$draw
	; we'll use all of our callee-saves
	sub	rsp, 72
	mov	[rsp], rdi
	mov	[rsp+8], rbx
	mov	[rsp+16], r12
	mov	[rsp+24], r13
	mov	[rsp+32], r14
	mov	[rsp+40], r15
	mov	[rsp+48], rbp
	mov	qword [rsp+56], 0
	mov	qword [rsp+64], 0

	; first up, blast our buffer by drawing our background
	call	tui_background$nvfill
	mov	rsi, [rsp]			; get our object back

	; calculate the offset of the last line of our text and attribute buffers into r14, r15
	mov	eax, [rsi+tui_width_ofs]
	mov	ecx, [rsi+tui_height_ofs]
	mul	ecx
	shl	eax, 2				; in bytes
	test	eax, eax	
	jz	.bailout			; width = 0 || height = 0

	mov	ecx, [rsi+tui_width_ofs]
	shl	ecx, 2
	sub	eax, ecx
	mov	r13d, ecx			; save our width
	mov	r14, [rsi+tui_text_ofs]
	add	r14, rax
	mov	r15, [rsi+tui_attr_ofs]
	add	r15, rax
	; ok so now, r14 and r15 point to the start of the last line of our text

	; we'll use [rsp+64] as our line counter
	mov	r12d, [rsi+tui_height_ofs]	; limit for our reverse_foreach

	; if our text line that we have to draw is _less_ than our actual height, we need to go backward for the difference
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	r8, [_list_size]

	mov	eax, r12d
	sub	eax, r8d
	mul	r13d				; eax = (height - linecount) * width in bytes
	mov	r9, r14
	mov	r10, r15
	sub	r9, rax
	sub	r10, rax
	
	cmp	r12d, r8d
	cmova	r14, r9
	cmova	r15, r10

	; store our highlightchar and highlightcolor
	mov	eax, dword [rsi+tui_label_highlightchar_ofs]
	mov	[rsp+56], eax
	mov	eax, dword [rsi+tui_label_highlightcolor_ofs]
	mov	[rsp+60], eax

	; TODO: would this be better with a jump table?
	mov	eax, dword [rsi+tui_label_textalign_ofs]

	; if we are doing highlighting, we need to use different line placement functions
	cmp	dword [rsi+tui_label_highlightchar_ofs], 0
	jne	.highlighter

	cmp	eax, tui_textalign_left
	je	.drawleft
	cmp	eax, tui_textalign_center
	je	.drawcenter
	; else, right it is
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_right

	; a note here on my strategy... 
	; because we are making use of all our callee-saves, and because list$reverse_foreach_limit
	; blasts callee-saves for its own uses during list traversal, this means we can't guarantee
	; that the values will remain in tact _inside_ the foreach destination function
	; to combat this:
	; we are doing a ridiculous amount of unconditional jumping around
	; this is messier to follow of course, but effectively does the same thing
	; as list$reverse_foreach_limit

	; there are a lot of stack-heavier ways to do this.. TODO: someday when I am bored, give this
	; some more consideration

macro tui_label_doit {
	test	rbx, rbx
	jz	.bailout
	jmp	rbp
}

	tui_label_doit

macro tui_label_list_next {
	add	dword [rsp+64], 1		; increment our line counter by one
	sub	r14, r13			; decrement r14 by one line
	sub	r15, r13			; decrement r15 by one line
	sub	r12d, 1
	jz	.bailout
	mov	rbx, [rbx+_list_prevofs]
	test	rbx, rbx
	jz	.bailout
	jmp	rbp
}

	; so on entry: rbx is pointing at our list _item_
	; 		r14 is pointing at our current text destination
	;		r15 is pointing at our current attr destination
	;
	; r12d is our line limit, which is handled by tui_label_list_next
	; r13 is our line length in bytes
	; rbp is our iterator function address, unchanging
calign
.placeline_left:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	call	memcpy
	tui_label_list_next
calign
.placeline_left_highlight:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	push	rdx
	call	memcpy
	pop	rdx
	shr	rdx, 2				; our copy length back in characters
	mov	rdi, r14
	mov	rsi, r15
	mov	r8d, dword [rsp+56]		; our highlight char
	mov	r9d, dword [rsp+60]		; our highlight color
calign
.dohighlight:
	mov	eax, dword [rdi]
	cmp	eax, r8d
	je	.highlightchar
	add	rdi, 4
	add	rsi, 4
	sub	rdx, 1
	jnz	.dohighlight
	
	tui_label_list_next
calign
.highlightchar:
	mov	dword [rsi], r9d
	add	rdi, 4
	add	rsi, 4
	sub	rdx, 1
	jnz	.dohighlight
	
	tui_label_list_next
calign
.placeline_center:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; the length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	r11, r13
	sub	r11, rdx
	shr	r11, 1				; space leftover div 2 (in bytes)
	and	r11, not 3
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	add	rdi, r11
	call	memcpy
	tui_label_list_next

calign
.placeline_center_highlight:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; the length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	r11, r13
	sub	r11, rdx
	shr	r11, 1				; space leftover div 2 (in bytes)
	and	r11, not 3
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	add	rdi, r11
	push	r11 rdx
	call	memcpy
	pop	rdx r11
	shr	rdx, 2				; our copy length back in characters
	mov	rdi, r14
	mov	rsi, r15
	add	rdi, r11
	add	rsi, r11
	mov	r8d, dword [rsp+56]		; our highlight char
	mov	r9d, dword [rsp+60]		; our highlight color
	jmp	.dohighlight

calign
.placeline_right:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; the length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	r11, r13
	sub	r11, rdx			; space leftover
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	add	rdi, r11
	call	memcpy
	tui_label_list_next
calign
.placeline_right_highlight:
	mov	rdi, [rbx]			; the value in the list item (our line string)
	mov	rdx, [rdi]			; the length of the string
	shl	rdx, 2				; in bytes
	cmp	rdx, r13
	cmova	rdx, r13			; length of our copy
	mov	r11, r13
	sub	r11, rdx			; space leftover
	mov	rsi, rdi
	add	rsi, 8
	mov	rdi, r14
	add	rdi, r11
	push	r11 rdx
	call	memcpy
	pop	rdx r11
	shr	rdx, 2				; our copy length back in characters
	mov	rdi, r14
	mov	rsi, r15
	add	rdi, r11
	add	rsi, r11
	mov	r8d, dword [rsp+56]		; our highlight char
	mov	r9d, dword [rsp+60]		; our highlight color
	jmp	.dohighlight

calign
.drawleft:
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_left
	tui_label_doit
calign
.drawcenter:
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_center
	tui_label_doit
calign
.highlighter:
	cmp	eax, tui_textalign_left
	je	.drawleft_highlight
	cmp	eax, tui_textalign_center
	je	.drawcenter_highlight
	; else, right it is
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_right_highlight
	tui_label_doit
calign
.drawleft_highlight:
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_left_highlight
	tui_label_doit
calign
.drawcenter_highlight:
	mov	rdi, [rsi+tui_label_lines_ofs]
	mov	rbx, [_list_last]
	mov	rbp, .placeline_center_highlight
	tui_label_doit
calign
.bailout:
	mov	rdi, [rsp]		; get our object back
	mov	rsi, [rdi]		; get our vtable
	call	qword [rsi+tui_vupdatedisplaylist]

	mov	rbx, [rsp+8]
	mov	r12, [rsp+16]
	mov	r13, [rsp+24]
	mov	r14, [rsp+32]
	mov	r15, [rsp+40]
	mov	rbp, [rsp+48]
	add	rsp, 72
	epilog
end if