Files
aqhomecontrol/avr/modules/lcd2/gui/base/guiapp.asm
2026-04-14 23:53:54 +02:00

776 lines
18 KiB
NASM

; ***************************************************************************
; copyright : (C) 2026 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_GUI2_GUIAPP_ASM
#define AQH_AVR_GUI2_GUIAPP_ASM
; ***************************************************************************
; defines
.equ GUIAPP_WINDOWSTACK_ENTRIES = 8
; Widget in flash
.equ GUIAPP_OFFS_BEGIN = OBJECT_SIZE
.equ GUIAPP_OFFS_ROOTWINDOW_LO = GUIAPP_OFFS_BEGIN+0
.equ GUIAPP_OFFS_ROOTWINDOW_HI = GUIAPP_OFFS_BEGIN+1
.equ GUIAPP_OFFS_SCREENSAVER_LO = GUIAPP_OFFS_BEGIN+2
.equ GUIAPP_OFFS_SCREENSAVER_HI = GUIAPP_OFFS_BEGIN+3
.equ GUIAPP_OFFS_CURRENTWINDOW_LO = GUIAPP_OFFS_BEGIN+4
.equ GUIAPP_OFFS_CURRENTWINDOW_HI = GUIAPP_OFFS_BEGIN+5
.equ GUIAPP_OFFS_TOUCHWIDGET_LO = GUIAPP_OFFS_BEGIN+6
.equ GUIAPP_OFFS_TOUCHWIDGET_HI = GUIAPP_OFFS_BEGIN+7
.equ GUIAPP_OFFS_GUITIMER = GUIAPP_OFFS_BEGIN+8
.equ GUIAPP_OFFS_WINDOWSTACK_POS = GUIAPP_OFFS_BEGIN+9
.equ GUIAPP_OFFS_WINDOWSTACK_BEGIN = GUIAPP_OFFS_BEGIN+10
.equ GUIAPP_SIZE = GUIAPP_OFFS_WINDOWSTACK_BEGIN+(GUIAPP_WINDOWSTACK_ENTRIES*2)
.equ GUIAPP_GUITIMER = 2
; ***************************************************************************
; data
.dseg
guiapp_touch_event:
.byte WIDGET_TOUCH_SIZE
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine GuiApp_new @global
;
; @return CFLAG set of okay, cleared otherwise
; @return Y address of newly created object
; @param r16 value for OBJECT_OFFS_OPTS
; @clobbers any
GuiApp_new:
ldi r24, LOW(GUIAPP_SIZE)
ldi r25, HIGH(GUIAPP_SIZE)
bigcall Object_Alloc ; (!r16, !r17)
brcc GuiApp_new_ret
rcall GuiApp_Init ; (r16, r17, X)
sec
GuiApp_new_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_Init @global
;
; @param Y address of object in SDRAM
; @param r16 value for OBJECT_OFFS_OPTS
; @clobbers r16, r17, X
GuiApp_Init:
; call base class
bigcall OBJ_Init ; (r16, r17, X)
; set default signal map
ldi r16, LOW(GuiApp_DefaultSignalmap*2)
std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
ldi r16, HIGH(GuiApp_DefaultSignalmap*2)
std Y+OBJECT_OFFS_SIGNALMAP_HI, r16
; create root window
push yl
push yh
mov xl, yl
mov xh, yh
ldi r16, 0 ; opts
ldi r17, 0 ; pack
bigcall RootWindow_new
; always visible
ldd r16, Y+OBJECT_OFFS_FLAGS
sbr r16, (1<<WIDGET_FLAGS_VISIBLE_BIT) | (1<<WIDGET_FLAGS_LAYOUT_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
std Y+OBJECT_OFFS_FLAGS, r16
mov xl, yl
mov xh, yh
pop yh
pop yl
brcc GuiApp_Init_ret
std Y+GUIAPP_OFFS_ROOTWINDOW_LO, xl
std Y+GUIAPP_OFFS_ROOTWINDOW_HI, xh
ldi r16, GUIAPP_GUITIMER
std Y+GUIAPP_OFFS_GUITIMER, r16
GuiApp_Init_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_Every100ms @global
;
; @param Y address of object in SDRAM
GuiApp_Every100ms:
ldi r16, OBJECT_SIGNAL_TIMER
clr r17
bigcall OBJ_HandleSignal
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_MsgReceived @global
;
; @param Y address of object in SDRAM
GuiApp_MsgReceived:
ldi r16, OBJECT_SIGNAL_RECVMSG
clr r17
bigcall OBJ_HandleSignal
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_PreventScreenSaver @global
;
; @param Y address of object in SDRAM
; @clobbers any, !Y
GuiApp_PreventScreenSaver:
rcall GuiApp_GetScreenSaver
brcc GuiApp_PreventScreenSaver_ret
push yl
push yh
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_KEEPALIVE
clr r17
bigcall OBJ_HandleSignal
pop yh
pop yl
GuiApp_PreventScreenSaver_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_GrabTouchEvents
;
; @param Y address of object in SDRAM
; @param X byte address of widget grabbing touch events
; @return CFLAG set if grabbed, cleared otherwise
; @clobbers R16, R17
GuiApp_GrabTouchEvents:
ldd r16, Y+GUIAPP_OFFS_TOUCHWIDGET_LO
ldd r17, Y+GUIAPP_OFFS_TOUCHWIDGET_HI
or r16, r17
clc
brne GuiApp_GrabTouchEvents_ret ; only grab if not already grabbed!
std Y+GUIAPP_OFFS_TOUCHWIDGET_LO, xl
std Y+GUIAPP_OFFS_TOUCHWIDGET_HI, xh
sec
GuiApp_GrabTouchEvents_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_UngrabTouchEvents
;
; @param Y address of object in SDRAM
; @param X byte address of widget ungrabbing touch events
; @return CFLAG set if ungrabbed, cleared otherwise
; @clobbers R16, R17
GuiApp_UngrabTouchEvents:
ldd r16, Y+GUIAPP_OFFS_TOUCHWIDGET_LO
ldd r17, Y+GUIAPP_OFFS_TOUCHWIDGET_HI
eor r16, xl
eor r17, xh
or r16, r17
clc
brne GuiApp_UngrabTouchEvents_ret ; only ungrab same widget!
clr r16
std Y+GUIAPP_OFFS_TOUCHWIDGET_LO, r16
std Y+GUIAPP_OFFS_TOUCHWIDGET_HI, r16
sec
GuiApp_UngrabTouchEvents_ret:
ret
; @end
; ***************************************************************************
; getters and setters
; ---------------------------------------------------------------------------
; @routine GuiApp_GetRootWindow @global
;
; @param Y address of object in SDRAM
; @param CFLAG set if response is not a NULL pointer
; @return r19:r18 root window
; @clobbers none
GuiApp_GetRootWindow:
ldd r18, Y+GUIAPP_OFFS_ROOTWINDOW_LO
ldd r19, Y+GUIAPP_OFFS_ROOTWINDOW_HI
sec
tst r18
brne GuiApp_GetRootWindow_ret
tst r19
brne GuiApp_GetRootWindow_ret
clc
GuiApp_GetRootWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_SetRootWindow @global
;
; @param Y address of object in SDRAM
; @param X address of new window
GuiApp_SetRootWindow:
std Y+GUIAPP_OFFS_ROOTWINDOW_LO, xl
std Y+GUIAPP_OFFS_ROOTWINDOW_HI, xh
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_GetCurrentWindow @global
;
; @param Y address of object in SDRAM
; @return CFLAG set if response is not a NULL pointer
; @return r19:r18 current window
; @clobbers none
GuiApp_GetCurrentWindow:
ldd r18, Y+GUIAPP_OFFS_CURRENTWINDOW_LO
ldd r19, Y+GUIAPP_OFFS_CURRENTWINDOW_HI
sec
tst r18
brne GuiApp_GetCurrentWindow_ret
tst r19
brne GuiApp_GetCurrentWindow_ret
clc
GuiApp_GetCurrentWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_GetScreenSaver @global
;
; @param Y address of object in SDRAM
; @param CFLAG set if response is not a NULL pointer
GuiApp_GetScreenSaver:
ldd r18, Y+GUIAPP_OFFS_SCREENSAVER_LO
ldd r19, Y+GUIAPP_OFFS_SCREENSAVER_HI
sec
tst r18
brne GuiApp_GetScreenSaver_ret
tst r19
brne GuiApp_GetScreenSaver_ret
clc
GuiApp_GetScreenSaver_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_SetScreenSaver @global
;
; @param Y address of object in SDRAM
; @param X address of new window
GuiApp_SetScreenSaver:
std Y+GUIAPP_OFFS_SCREENSAVER_LO, xl
std Y+GUIAPP_OFFS_SCREENSAVER_HI, xh
ret
; @end
; ***************************************************************************
; signal handlers
; ---------------------------------------------------------------------------
; @routine GuiApp_OnDestroy @global
;
; @param Y address of object in SDRAM
; @return CFLAG set if signal handled
; @clobbers any, !Y
GuiApp_OnDestroy:
; TODO (destroy rootWindow)
; Clear CFLAG so that destroy handlers of base classes are called
; after this routine returns. This way the last routine called will be
; OBJ_OnDestroy which then frees the whole SDRAM object on heap.
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_OnTimer
;
; @param Y address of object in SDRAM
; @clobbers any, !Y
GuiApp_OnTimer:
rcall guiAppCheckTouch ; (any, !Y)
rcall guiAppSendTimerEvents ; (any, !Y)
rcall guiAppCheckSendGuiEvents ; (any, !Y)
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_OnMsgReceived @global
;
; @param Y address of object in SDRAM
; @param X pointer to message received in SDRAM
GuiApp_OnMsgReceived:
rcall guiAppSendRootMsgEvents
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_OnTouch
;
; @param Y address of object in SDRAM
; @param X pointer to touch object (see @ref WIDGET_TOUCH_OFFS_X_LO)
; @clobbers any, !Y
GuiApp_OnTouch:
rcall guiAppSendTouchEvents
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_EnterWindow
;
; @param Y address of object in SDRAM
; @param X main window to enter
; @return CFLAG set if new window active
GuiApp_EnterWindow:
rcall GuiApp_GetCurrentWindow ; r19:r18=current window
brcc GuiApp_EnterWindow_l1 ; jmp if no current window
push xl
push xh
push yl
push yh
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_HIDE
clr r17
bigcall OBJ_TreeHandleSignalChildenFirst
mov xl, yl
mov xh, yh
pop yh
pop yl
rcall guiAppPushMainWindow ; push current window onto stack
pop xh
pop xl
brcc GuiApp_EnterWindow_ret
GuiApp_EnterWindow_l1:
rcall guiAppSetCurrentWindow
push yl
push yh
mov yl, xl
mov yh, xh
ldi r16, WIDGET_SIGNAL_SHOW
clr r17
bigcall OBJ_TreeHandleSignal
pop yh
pop yl
sec
GuiApp_EnterWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine GuiApp_LeaveWindow
;
; @param Y address of object in SDRAM
GuiApp_LeaveWindow:
rcall GuiApp_GetCurrentWindow ; r19:r18=current window
brcc GuiApp_LeaveWindow_ret ; jmp if no current window
push yl
push yh
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_HIDE
clr r17
bigcall OBJ_TreeHandleSignalChildenFirst
pop yh
pop yl
GuiApp_LeaveWindow_l1:
rcall guiAppPopMainWindow ; X=popped window
brcc GuiApp_LeaveWindow_noPrevious
rcall guiAppSetCurrentWindow
push yl
push yh
mov yl, xl
mov yh, xh
ldi r16, WIDGET_SIGNAL_SHOW
clr r17
bigcall OBJ_TreeHandleSignal
pop yh
pop yl
rjmp GuiApp_LeaveWindow_ret
GuiApp_LeaveWindow_noPrevious:
clr xl
clr xh
rcall guiAppSetCurrentWindow
GuiApp_LeaveWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppSetCurrentWindow
;
; @param Y address of object in SDRAM
; @param X address of new window
guiAppSetCurrentWindow:
std Y+GUIAPP_OFFS_CURRENTWINDOW_LO, xl
std Y+GUIAPP_OFFS_CURRENTWINDOW_HI, xh
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppPushMainWindow
;
; @param Y address of object in SDRAM
; @param X window to push on stack and activate
; @return CFLAG set if pushed, cleared on error
guiAppPushMainWindow:
ldd r16, Y+GUIAPP_OFFS_WINDOWSTACK_POS
cpi r16, GUIAPP_WINDOWSTACK_ENTRIES
brcc GuiApp_PushMainWindow_ret
mov zl, yl
mov zh, yh
adiw zh:zl, GUIAPP_OFFS_WINDOWSTACK_BEGIN
clr r17
lsl r16
rol r17
add zl, r16
adc zh, r17
st Z+, xl
st Z, xh
ldd r16, Y+GUIAPP_OFFS_WINDOWSTACK_POS
inc r16
std Y+GUIAPP_OFFS_WINDOWSTACK_POS, r16
sec
GuiApp_PushMainWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppPopMainWindow
;
; @param Y address of object in SDRAM
; @return CFLAG set if popped, cleared on error
; @return X window popped from stack
guiAppPopMainWindow:
ldd r16, Y+GUIAPP_OFFS_WINDOWSTACK_POS
tst r16
clc
breq guiAppPopMainWindow_ret
dec r16
std Y+GUIAPP_OFFS_WINDOWSTACK_POS, r16
mov zl, yl
mov zh, yh
adiw zh:zl, GUIAPP_OFFS_WINDOWSTACK_BEGIN
clr r17
lsl r16
rol r17
add zl, r16
adc zh, r17
ld xl, Z+
ld xh, Z
sec
guiAppPopMainWindow_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppSendTimerEvents
;
; @param Y ptr to GUIAPP
; @clobbers any, !Y
guiAppSendTimerEvents:
ldi r16, OBJECT_SIGNAL_TIMER
clr r17
ldi r20, (1<<OBJECT_OPTS_TIMER_BIT)
ldi r21, (1<<OBJECT_OPTS_TIMER_BIT)
rcall guiAppSendRootEventsIfOpts
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppCheckSendGuiEvents
;
; Send GUI events to current window if timer elapsed.
;
; @param Y ptr to GUIAPP
; @clobbers any, !Y
guiAppCheckSendGuiEvents:
push yl
push yh
rcall GuiApp_GetRootWindow
brcc guiAppCheckSendGuiEvents_done
ldd r16, Y+GUIAPP_OFFS_GUITIMER
tst r16
breq guiAppCheckSendGuiEvents_done
dec r16
std Y+GUIAPP_OFFS_GUITIMER, r16
brne guiAppCheckSendGuiEvents_done
; timer elapsed, send layout events, draw events
push yl
push yh
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_LAYOUT
clr r17
ldi r20, (1<<WIDGET_FLAGS_VISIBLE_BIT) | (1<<WIDGET_FLAGS_LAYOUT_BIT)
bigcall OBJ_TreeHandleSignalIfFlagsSet ; (any, !Y)
ldi r16, WIDGET_SIGNAL_DRAW
clr r17
ldi r20, (1<<WIDGET_FLAGS_VISIBLE_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
bigcall OBJ_TreeHandleSignalIfFlagsSet ; (any, !Y)
pop yh
pop yl
ldi r16, GUIAPP_GUITIMER
std Y+GUIAPP_OFFS_GUITIMER, r16
guiAppCheckSendGuiEvents_done:
pop yh
pop yl
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppSendTouchEvents
;
; Send touch events to current window or grabbing widget.
;
; @param Y ptr to GUIAPP
; @param X pointer to touch object (see @ref WIDGET_TOUCH_OFFS_X_LO)
; @clobbers any, !Y
guiAppSendTouchEvents:
push yl
push yh
ldd r18, Y+GUIAPP_OFFS_TOUCHWIDGET_LO
ldd r19, Y+GUIAPP_OFFS_TOUCHWIDGET_HI
mov r16, r18
or r16, r19
breq guiAppSendTouchEvents_sendToAll
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_TOUCH
clr r17
bigcall OBJ_HandleSignal
rjmp guiAppSendTouchEvents_done
guiAppSendTouchEvents_sendToAll:
rcall GuiApp_GetCurrentWindow ; r19:r18=current window (none)
brcc guiAppSendTouchEvents_done
; send touch signal
mov yl, r18
mov yh, r19
ldi r16, WIDGET_SIGNAL_TOUCH
clr r17
ldi r20, (1<<WIDGET_OPTS_INPUT_BIT)
ldi r21, (1<<WIDGET_OPTS_INPUT_BIT)
bigcall OBJ_TreeHandleSignalIfMatchingOpts
guiAppSendTouchEvents_done:
pop yh
pop yl
; send keepAlive to screensaver
rcall GuiApp_PreventScreenSaver
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppSendRootMsgEvents
;
; Send touch events to root window.
;
; @param Y ptr to GUIAPP
; @param X pointer to message received in SDRAM
; @clobbers any, !Y
guiAppSendRootMsgEvents:
ldi r16, OBJECT_SIGNAL_RECVMSG
adiw xh:xl, NETMSG_OFFS_CMD
ld r17, X
sbiw xh:xl, NETMSG_OFFS_CMD
ldi r20, (1<<OBJECT_OPTS_MSGRECV_BIT)
ldi r21, (1<<OBJECT_OPTS_MSGRECV_BIT)
rcall guiAppSendRootEventsIfOpts
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppSendRootEventsIfOpts
;
; Send events to all widgets with matching OPTS_LO.
;
; @param Y ptr to GUIAPP
; @param R16 signal number
; @param R17 selector
; @param xl param1
; @param xh param2
; @param r20 mask for OBJECT_OFFS_OPTS
; @param r21 value for OBJECT_OFFS_OPTS to match
; @clobbers any, !Y
guiAppSendRootEventsIfOpts:
push yl
push yh
rcall GuiApp_GetRootWindow ; r19:r18=root window (none)
brcc guiAppSendRootEventsIfOpts_done
; send signal to root window and all below
mov yl, r18
mov yh, r19
bigcall OBJ_TreeHandleSignalIfMatchingOpts
guiAppSendRootEventsIfOpts_done:
pop yh
pop yl
ret
; @end
; ---------------------------------------------------------------------------
; @routine guiAppCheckTouch
;
; @param Y ptr to GUIAPP
; @clobbers any, !Y
guiAppCheckTouch:
bigcall Display_InputGetState ; r16=flags, r5:r4=x, r7:r6=y
mov r17, r16
andi r17, (1<<DISPLAY_IFLAGS_CHGCOORD_BIT) | (1<<DISPLAY_IFLAGS_CHGPRESS_BIT)
breq guiAppCheckTouch_ret
ldi xl, LOW(guiapp_touch_event)
ldi xh, HIGH(guiapp_touch_event)
st X+, r4 ; X
st X+, r5
st X+, r6 ; Y
st X+, r7
st X, r16 ; flags
sbiw xh:xl, 4 ; (4 times incremented)
; send touch signal
ldi r16, WIDGET_SIGNAL_TOUCH
clr r17
bigcall OBJ_HandleSignal
guiAppCheckTouch_ret:
ret
; @end
; ***************************************************************************
; data in FLASH
GuiApp_DefaultSignalmap:
; header
.dw Object_DefaultSignalmap*2 ; next table to use
; entries
.db 0, OBJECT_SIGNAL_TIMER, LOW(GuiApp_OnTimer), HIGH(GuiApp_OnTimer)
.db 0, OBJECT_SIGNAL_RECVMSG, LOW(GuiApp_OnMsgReceived), HIGH(GuiApp_OnMsgReceived)
.db 0, WIDGET_SIGNAL_TOUCH, LOW(GuiApp_OnTouch), HIGH(GuiApp_OnTouch)
.db 0, OBJECT_SIGNAL_DESTROY, LOW(GuiApp_OnDestroy), HIGH(GuiApp_OnDestroy)
.db 0, 0, 0, 0 ; end of table
#endif