Compare commits

..
Author SHA1 Message Date
Burer ef1eceaa6c data: point the desktop menu to TASKMAN
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 21s
Test PR / Build (en_US) (pull_request) Successful in 2m56s
Test PR / Build (ru_RU) (pull_request) Successful in 3m1s
Test PR / Build (es_ES) (pull_request) Successful in 3m1s
Assisted-by: Claude:claude-opus-5-5
2026-10-04 09:25:15 +03:00
Burer 3f3770fc57 apps/taskman: add a readme
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 20s
Test PR / Build (en_US) (pull_request) Successful in 1m51s
Test PR / Build (es_ES) (pull_request) Successful in 1m57s
Test PR / Build (ru_RU) (pull_request) Successful in 2m1s
Assisted-by: Claude:claude-opus-5-5
2026-10-03 22:36:40 +03:00
Burer 9fe9b676c1 data: replace CPU and SYSMON with TASKMAN
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 24s
Test PR / Build (en_US) (pull_request) Successful in 2m10s
Test PR / Build (es_ES) (pull_request) Successful in 2m16s
Test PR / Build (ru_RU) (pull_request) Successful in 2m23s
Put TASKMAN in the image instead of CPU and SYSMON and point the
menu, the system panel and the KolibriNext dock and desktop to it.

Assisted-by: Claude:claude-opus-5-5
2026-10-03 22:02:00 +03:00
Burer 3110deedd5 kernel: start TASKMAN on Ctrl+Alt+Del
Assisted-by: Claude:claude-opus-5-5
2026-10-03 22:01:59 +03:00
Burer 0486f294ce apps/taskman: add a task manager
One program with five tabs instead of CPU and SYSMON:

- Processes: threads with CPU and memory, sortable; Details, End,
  Run..., system threads on request.
- Performance: CPU, temperature, memory and network graphs; the CPU
  sensor (Intel, AMD, Hygon, VIA, Zhaoxin) or a board chip.
- Disks: partitions with labels, used space of memory disks; Open.
- Drivers: drivers loaded by the kernel; Load...
- Autorun: /sys/settings/autorun.dat, a program turned on or off.

The window and lists follow the system colors; en_US, ru_RU, es_ES.
The kpacked binary fits in 12 sectors.

Assisted-by: Claude:claude-opus-5-5
2026-10-03 22:01:59 +03:00
30 changed files with 3900 additions and 621 deletions

No files matched your search

+1 -2
View File
@@ -421,7 +421,6 @@ tup.append_table(img_files, {
{"CALENDAR", VAR_PROGS .. "/system/calendar/calendar"},
{"COLRDIAL", VAR_PROGS .. "/system/colrdial/color_dialog"},
{"CROPFLAT", VAR_PROGS .. "/system/cropflat/cropflat"},
{"CPU", VAR_PROGS .. "/system/cpu/cpu"},
{"CPUID", VAR_PROGS .. "/testing/cpuid/CPUID"},
{"DOCPACK", VAR_PROGS .. "/system/docpack/docpack"},
{"DEFAULT.SKN", VAR_SKINS .. "/../skins/Leency/Shkvorka/Shkvorka.skn"},
@@ -446,6 +445,7 @@ tup.append_table(img_files, {
{"SCRSHOOT", VAR_PROGS .. "/media/scrshoot/scrshoot"},
{"SETUP", VAR_PROGS .. "/system/setup/setup"},
{"SKINCFG", VAR_PROGS .. "/system/skincfg/skincfg"},
{"TASKMAN", VAR_PROGS .. "/system/taskman/taskman"},
{"TERMINAL", VAR_PROGS .. "/system/terminal/terminal"},
{"TEST", VAR_PROGS .. "/testing/protection/test"},
{"TINYPAD", VAR_PROGS .. "/develop/tinypad/tinypad"},
@@ -694,7 +694,6 @@ tup.append_table(img_files, {
{"BBENCH", VAR_PROGS .. "/cmm/bbench/bbench.com"},
{"SEARCH", VAR_PROGS .. "/cmm/misc/search.com"},
{"SYSPANEL", VAR_PROGS .. "/cmm/misc/software_widget.com"},
{"SYSMON", VAR_PROGS .. "/cmm/sysmon/sysmon.com"},
{"TMPDISK", VAR_PROGS .. "/cmm/tmpdisk/tmpdisk.com"},
{"DEVELOP/CLIPVIEW", VAR_PROGS .. "/cmm/clipview/clipview.com"},
{"DEVELOP/MENU", VAR_PROGS .. "/cmm/menu/menu.com"},
+1 -1
View File
@@ -1 +1 @@
[@] location=3 fsize=0 ashow=1 [AppPlus] path=/sys/app_plus param= icon=27 $=1 [KFAR] path=/sys/file managers/kfar param= icon=16 [Pixie2] path=/sys/media/pixie param= icon=65 [IconEdit] path=/sys/media/iconedit param= icon=98 [Calc] path=/sys/calc param= icon=4 [Notes] path=/kolibrios/utils/notes param= icon=117 [Dictionary] path=/kolibrios/utils/dicty.kex param= icon=79 [FB2Read] path=/sys/fb2read param= icon=44 [Animage] path=/sys/media/animage param= icon=15 [RDSave] path=/sys/rdsave param= icon=17 $=1 [Volume] path=/sys/@volume param= icon=64 [NetStatus] path=/sys/network/netstat param= icon=33 [SysMon] path=/sys/sysmon param= icon=57 [SysPanel] path=/sys/syspanel param= icon=6
[@] location=3 fsize=0 ashow=1 [AppPlus] path=/sys/app_plus param= icon=27 $=1 [KFAR] path=/sys/file managers/kfar param= icon=16 [Pixie2] path=/sys/media/pixie param= icon=65 [IconEdit] path=/sys/media/iconedit param= icon=98 [Calc] path=/sys/calc param= icon=4 [Notes] path=/kolibrios/utils/notes param= icon=117 [Dictionary] path=/kolibrios/utils/dicty.kex param= icon=79 [FB2Read] path=/sys/fb2read param= icon=44 [Animage] path=/sys/media/animage param= icon=15 [RDSave] path=/sys/rdsave param= icon=17 $=1 [Volume] path=/sys/@volume param= icon=64 [NetStatus] path=/sys/network/netstat param= icon=33 [TaskMan] path=/sys/taskman param= icon=57 [SysPanel] path=/sys/syspanel param= icon=6
+1 -1
View File
@@ -1 +1 @@
[rbmenu] Диспетчер процессов=/sys/cpu Настройка внешнего вида=/kolibrios/utils/appearance ;Настройка цветов=/sys/skincfg ;Сплошной фон=/sys/media/palitra [01] name=FILES path=/sys/File Managers/EOLITE param= ico=7 x=4 y=21 [02] name=FPLAY path=/kolibrios/media/fplay param= ico=96 x=65 y=21 [03] name=GAMES path=/sys/allgames param= ico=35 x=125 y=21 [04] name=TINYPAD path=/sys/tinypad param= ico=9 x=4 y=96 [05] name=zSEA path=/kolibrios/media/zsea/zsea param= ico=46 x=65 y=96 [06] name=WEBVIEW path=/sys/network/webview param= ico=31 x=125 y=96 ; ========= ; RIGHT TOP ; ========= [10] name=DEBUG path=/SYS/DEVELOP/MTDBG param= ico=39 x=-204 y=21 [11] name=BOARD path=/SYS/DEVELOP/BOARD param= ico=19 x=-140 y=21 [12] name=KPACK path=/SYS/KPACK param= ico=99 x=-77 y=21 [13] name=DIFF path=/sys/develop/diff param= ico=84 x=-203 y=96 [14] name=FASM path=/SYS/DEVELOP/FASM param= ico=10 x=-141 y=96 [15] name=DOCPACK path=/sys/network/webview param=/kolibrios/res/guide/guide.htm ico=8 x=-76 y=96 [16] name=SHELL path=/SYS/SHELL param= ico=1 x=-203 y=166 [17] name=HEXEDIT path=/SYS/DEVELOP/HEED param= ico=22 x=-141 y=166 [18] name=CEDIT path=/sys/develop/cedit param= ico=58 x=-74 y=166
[rbmenu] Диспетчер задач=/sys/taskman Настройка внешнего вида=/kolibrios/utils/appearance ;Настройка цветов=/sys/skincfg ;Сплошной фон=/sys/media/palitra [01] name=FILES path=/sys/File Managers/EOLITE param= ico=7 x=4 y=21 [02] name=FPLAY path=/kolibrios/media/fplay param= ico=96 x=65 y=21 [03] name=GAMES path=/sys/allgames param= ico=35 x=125 y=21 [04] name=TINYPAD path=/sys/tinypad param= ico=9 x=4 y=96 [05] name=zSEA path=/kolibrios/media/zsea/zsea param= ico=46 x=65 y=96 [06] name=WEBVIEW path=/sys/network/webview param= ico=31 x=125 y=96 ; ========= ; RIGHT TOP ; ========= [10] name=DEBUG path=/SYS/DEVELOP/MTDBG param= ico=39 x=-204 y=21 [11] name=BOARD path=/SYS/DEVELOP/BOARD param= ico=19 x=-140 y=21 [12] name=KPACK path=/SYS/KPACK param= ico=99 x=-77 y=21 [13] name=DIFF path=/sys/develop/diff param= ico=84 x=-203 y=96 [14] name=FASM path=/SYS/DEVELOP/FASM param= ico=10 x=-141 y=96 [15] name=DOCPACK path=/sys/network/webview param=/kolibrios/res/guide/guide.htm ico=8 x=-76 y=96 [16] name=SHELL path=/SYS/SHELL param= ico=1 x=-203 y=166 [17] name=HEXEDIT path=/SYS/DEVELOP/HEED param= ico=22 x=-141 y=166 [18] name=CEDIT path=/sys/develop/cedit param= ico=58 x=-74 y=166
+1 -2
View File
@@ -23,8 +23,7 @@ Bus Disconnect=kbd,37
System Settings=setup,5
[Monitoring]
Processes=cpu,60
System Monitor=sysmon,57
Task Manager=taskman,57
Ghost Monitor=gmon,107
Network Status=network/netstat,33
AMDtemp=/k/utils/AMDtemp,128
+1 -1
View File
@@ -1,5 +1,5 @@
[rbmenu]
Processes=cpu
Task manager=taskman
Theme settings=skincfg
Background=/sys/media/palitra
System panel=syspanel
+1 -1
View File
@@ -75,7 +75,7 @@
56 Clipboard viewer |develop/clipview
62 Calendar |calendar
33 Online update * |/kolibrios/utils/osupdate
48 Process manager |cpu
48 Task manager |taskman
24 Terminal |terminal
05 Save ramdisk |rdsave
24 Shell |shell
+1 -1
View File
@@ -1,5 +1,5 @@
[rbmenu]
Processes=cpu
Task manager=taskman
Theme settings=skincfg
Background=/sys/media/palitra
System panel=syspanel
+1 -1
View File
@@ -82,7 +82,7 @@
16 Colores y Apariencia |skincfg
16 Atajos de teclado |mykey
#9 **** SENSORES
16 Procesos |cpu
16 Gestor de tareas |taskman
16 Dispositivos PCI |pcidev
16 CPUID |cpuid
16 Ghost Monitor |gmon
+1 -2
View File
@@ -23,8 +23,7 @@ Desconectar bus=kbd,37
Ajustes del sistema=setup,5
[Monitorizacion]
Procesos=cpu,60
Monitor del sistema=sysmon,57
Gestor de tareas=taskman,57
Ghost Monitor=gmon,107
Estado de red=network/netstat,33
AMDtemp=/k/utils/AMDtemp,128
+1 -1
View File
@@ -1,5 +1,5 @@
[rbmenu]
Диспетчер процессов=cpu
Диспетчер задач=taskman
Управление темой=skincfg
Настройка фона=/sys/media/palitra
Системная панель=syspanel
+1
View File
@@ -73,6 +73,7 @@
00 Файловые менеджеры > |@8
00 Спец. возможности > |@9
62 Календарь |calendar
48 Диспетчер задач |taskman
51 Виртуальные диски |tmpdisk
24 Терминал |terminal
24 Консоль SHELL |shell
+1 -2
View File
@@ -23,8 +23,7 @@ Hot Angles=HACONFIG,105
Системные настройки=setup,5
[Мониторинг]
Процессы=cpu,60
Системный монитор=sysmon,57
Диспетчер задач=taskman,57
Ghost Monitor=gmon,107
Статус сети=network/netstat,33
AMDtemp=/k/utils/AMDtemp,128
+74 -604
View File
@@ -38,7 +38,10 @@ DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors/events only
; 1 = per-second RX/TX counters and a frame log on the debug board
; 1 = print per-second RX/TX rate statistics to the debug board while
; traffic flows (cb/fr = transfers/frames, B = bytes, tx = frames sent,
; wnd = advertised TCP window). Costs a few counters per frame and one
; line of board output per second; invaluable for throughput debugging.
DEBUG_STATS = 0
include '../../struct.inc'
@@ -81,9 +84,6 @@ OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E
; NDIS packet filter: directed | multicast | all-multicast | broadcast
RNDIS_PACKET_FILTER = 0x0000000F
; With a host address of our own choosing (see random_mac) also
; promiscuous, as Linux always sets: the device never learns that address.
RNDIS_FILTER_PROMISC = 0x00000020
RNDIS_STATUS_MEDIA_CONNECT = 0x4001000B
RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000C
@@ -98,32 +98,13 @@ STATE_RUN = 4 ; registered, data path live
ETH_FRAME_MIN = 60 ; minimal ethernet frame (without FCS)
ETH_FRAME_MAX = 1514 ; maximal ethernet frame (without FCS)
RNDIS_HDR_SIZE = 44 ; REMOTE_NDIS_PACKET_MSG header
; Our MaxTransferSize and the RX buffer size: one frame, as in Linux.
; With 8192 phones split messages across transfers and we lost them.
RNDIS_RX_BUF = 2048
; Bulk IN transfers kept in flight: up to 32 frames per 10 ms wakeup
; of the USB thread (~38 Mbit/s). See the note in rndis_start.
RNDIS_RX_BUFFERS = 32
RNDIS_RX_BUF = 8192 ; our max transfer, advertised in INIT
RNDIS_RX_BUFFERS = 2 ; ping-pong pair for the bulk IN pipe
RNDIS_RX_MAX_ERRORS = 32 ; consecutive RX errors before receive
; stops (anti retry-storm)
; The same for the interrupt IN pipe (was an endless retry storm).
RNDIS_NOTIFY_MAX_ERRORS = 16
RNDIS_RSP_BUF = 1024 ; encapsulated response buffer
RNDIS_QUERY_LEN = 28 + 48 ; QUERY_MSG with a 48-byte input buffer,
; what Linux rndis_host sends; also the
; size of CmdBuf, the longest command
; Bulk OUT messages in flight, each in a slot of a preallocated pool
; (Kmalloc's 128 KB heap ran out). TX completions wait up to 10 ms for
; the USB thread, and 96 slots were still exhausted during uploads.
; A 2 KB slot never crosses a page, so 192 TRBs fit the 256-entry ring.
RNDIS_TX_MAX_PENDING = 192 ; slots, a multiple of 32 (free bitmap)
RNDIS_TX_SLOT = 2048 ; [context ptr][message <= 1559 bytes]
RNDIS_FLOWS = 8 ; TCP flows tracked by DEBUG_STATS (power of 2)
RNDIS_LOG_BURST = 40 ; frame-log lines allowed per second (DEBUG_STATS)
RNDIS_TX_MAX_PENDING = 32 ; max messages in flight on bulk OUT
RNDIS_GET_RETRIES = 200 ; response polls per command (see below)
; 1/100 s to wait for MEDIA_CONNECT before taking the link as up.
; A phone just switched to tethering drops the first DHCP DISCOVER.
RNDIS_LINK_WAIT = 100
; USB structures
struct usb_descr
@@ -196,8 +177,6 @@ struct rndis_dev ETH_DEVICE
InitState dd ? ; STATE_* of the init chain
DevMaxTx dd ? ; device's max transfer (from INIT_C)
LinkDown dd ? ; a MEDIA_DISCONNECT was indicated
LinkKnown dd ? ; the device has indicated its media state
LinkTimer dd ? ; TimerHS handle of the link-up fallback
TxPending dd ? ; messages in flight on bulk OUT
Dead dd ? ; set on disconnect/fatal error
@@ -208,16 +187,9 @@ struct rndis_dev ETH_DEVICE
KaPending dd ? ; a keepalive reply is in flight
GetRetries dd ? ; polls left for the current command
RxArea dd ? ; one KernelAlloc holding all RX buffers
RxBufs rd RNDIS_RX_BUFFERS ; bulk IN buffers, slices of RxArea
RxBufs rd RNDIS_RX_BUFFERS ; bulk IN buffers (KernelAlloc)
RxSize dd ? ; size of one buffer
TxArea dd ? ; KernelAlloc'ed pool of TX slots
TxFree rd RNDIS_TX_MAX_PENDING/32 ; bit set = slot free
RxErrRun dd ? ; consecutive failed RX transfers
NotifyErrRun dd ? ; consecutive failed notification polls
RxArmed dd ? ; transfers outstanding on the bulk IN
; pipe; zero means receive has died
LenErrNoted dd ? ; refused-frame lengths already logged
; per-second statistics, only maintained when DEBUG_STATS = 1
StatTicks dd ? ; last print time, timer ticks
@@ -227,24 +199,11 @@ struct rndis_dev ETH_DEVICE
StatTxPkts dd ? ; packets_tx snapshot at last print
StatWnd dd ? ; TCP receive window we advertised
; in the last outgoing TCP frame
StatTxHigh dd ? ; deepest the bulk OUT queue has been
StatTail dd ? ; received bytes no frame was made of
StatRxUni dd ? ; frames received addressed to our MAC
StatRxBc dd ? ; frames received as broadcast/multicast
LogTicks dd ? ; start of the current frame-log second
LogCount dd ? ; frame-log lines printed in it
StatLogDrop dd ? ; frame-log lines suppressed by the limit
StatGap dd ? ; TCP segments that arrived beyond a
; hole in their flow (loss/reorder)
StatRetx dd ? ; TCP segments with data already seen
; (the peer retransmitting)
FlowNext dd ? ; next slot of Flows to recycle
Flows rd RNDIS_FLOWS*2 ; [key][next expected seq]
SetupCmd rb 8 ; setup packet: SEND_ENCAPSULATED_CMD
SetupRsp rb 8 ; setup packet: GET_ENCAPSULATED_RSP
SetupKa rb 8 ; setup packet: keepalive reply
CmdBuf rb RNDIS_QUERY_LEN ; init chain command messages
CmdBuf rb 64 ; init chain command messages
KaBuf rb 16 ; keepalive reply message
NotifBuf rb 16 ; interrupt notification buffer
RspBuf rb RNDIS_RSP_BUF ; encapsulated responses
@@ -280,18 +239,6 @@ proc START c, reason:dword, cmdline:dword
jne .nothing
DEBUGF 2,"loading (RNDIS host network driver)\n"
; one random host address per boot (see random_mac)
rdtsc
xor eax, edx
imul eax, eax, 0x9E3779B1
mov dword [random_mac], eax
imul eax, eax, 0x85EBCA6B
rol eax, 13
mov word [random_mac+4], ax
and byte [random_mac], 0xFE ; unicast
or byte [random_mac], 0x02 ; locally administered
invoke RegUSBDriver, my_service, service_proc, usb_functions
.nothing:
@@ -746,16 +693,7 @@ proc rndis_notify_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword,
cmp [.status], USB_STATUS_STALL
je .ret ; don't hammer a stalled pipe
cmp [.status], 0
je .status_ok
; transient error: retry, but not for ever
inc [ebx+rndis_dev.NotifyErrRun]
cmp [ebx+rndis_dev.NotifyErrRun], RNDIS_NOTIFY_MAX_ERRORS
jb .rearm
DEBUGF 2,"notification pipe stopped after %u errors (status %u)\n", \
[ebx+rndis_dev.NotifyErrRun], [.status]
ret
.status_ok:
mov [ebx+rndis_dev.NotifyErrRun], 0
jne .rearm ; transient error: retry
cmp [.length], 8
jb .rearm
@@ -825,23 +763,17 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
mov [ebx+rndis_dev.DevMaxTx], eax
DEBUGF 2,"initialized, device max transfer %u\n", eax
; query the permanent MAC address, with an unused input buffer as Linux
; sends it: QEMU's usb-net stalls a query without one
; query the permanent MAC address
lea edi, [ebx+rndis_dev.CmdBuf]
push edi
xor eax, eax
mov ecx, RNDIS_QUERY_LEN / 4
rep stosd
pop edi
mov dword [edi], RNDIS_MSG_QUERY
mov dword [edi+4], RNDIS_QUERY_LEN ; MessageLength
mov dword [edi+4], 28 ; MessageLength
mov dword [edi+8], 2 ; RequestID
mov dword [edi+12], OID_802_3_PERMANENT_ADDRESS
mov dword [edi+16], RNDIS_QUERY_LEN - 28 ; InformationBufferLength
mov dword [edi+16], 0 ; InformationBufferLength
mov dword [edi+20], 20 ; InformationBufferOffset
mov dword [edi+24], 0 ; DeviceVcHandle
mov [ebx+rndis_dev.InitState], STATE_MAC
mov eax, RNDIS_QUERY_LEN
mov eax, 28
call rndis_send_command
test eax, eax
jz .fail
@@ -872,24 +804,6 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
[ebx+rndis_dev.mac+0]:2,[ebx+rndis_dev.mac+1]:2,[ebx+rndis_dev.mac+2]:2,\
[ebx+rndis_dev.mac+3]:2,[ebx+rndis_dev.mac+4]:2,[ebx+rndis_dev.mac+5]:2
; A locally administered address is the same on every plug-in, so each
; boot repeated the previous boot's connections into stale phone NAT
; state. Replace it with a random one per boot, as Linux does.
mov edx, RNDIS_PACKET_FILTER
test byte [ebx+rndis_dev.mac], 0x02
jz .mac_done
mov eax, dword [random_mac]
mov dword [ebx+rndis_dev.mac], eax
mov ax, word [random_mac+4]
mov word [ebx+rndis_dev.mac+4], ax
or edx, RNDIS_FILTER_PROMISC
push edx
DEBUGF 2,"using random MAC %x-%x-%x-%x-%x-%x\n",\
[ebx+rndis_dev.mac+0]:2,[ebx+rndis_dev.mac+1]:2,[ebx+rndis_dev.mac+2]:2,\
[ebx+rndis_dev.mac+3]:2,[ebx+rndis_dev.mac+4]:2,[ebx+rndis_dev.mac+5]:2
pop edx
.mac_done:
; enable reception: set the packet filter
lea edi, [ebx+rndis_dev.CmdBuf]
mov dword [edi], RNDIS_MSG_SET
@@ -899,7 +813,7 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
mov dword [edi+16], 4 ; InformationBufferLength
mov dword [edi+20], 20 ; InformationBufferOffset
mov dword [edi+24], 0 ; DeviceVcHandle
mov dword [edi+28], edx
mov dword [edi+28], RNDIS_PACKET_FILTER
mov [ebx+rndis_dev.InitState], STATE_FILTER
mov eax, 32
call rndis_send_command
@@ -950,13 +864,11 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
je .media_up
cmp eax, RNDIS_STATUS_MEDIA_DISCONNECT
jne .done
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.LinkDown], 1
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
DEBUGF 2,"link down\n"
jmp .media_changed
.media_up:
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.LinkDown], 0
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
DEBUGF 2,"link up\n"
@@ -1014,28 +926,6 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
endp
; The link-up fallback (see RNDIS_LINK_WAIT): the device said nothing
; about its media in time, take the link as up. Runs from the kernel's
; timer list, the context is the one given to TimerHS.
proc rndis_link_timer stdcall uses ebx, .ctx:dword
mov ebx, [.ctx]
mov [ebx+rndis_dev.LinkTimer], 0
cmp [ebx+rndis_dev.Dead], 0
jne .ret
cmp [ebx+rndis_dev.LinkKnown], 0
jne .ret ; the device spoke meanwhile
mov [ebx+rndis_dev.LinkKnown], 1
DEBUGF 2,"no media indication, assuming link up\n"
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
push ebx
invoke NetLinkChanged
pop ebx
.ret:
ret
endp
; Init chain complete: allocate the receive buffers, register in the
; network stack, bring the link up and start receiving.
; IN: ebx = device context
@@ -1044,32 +934,15 @@ proc rndis_start
mov [ebx+rndis_dev.InitState], STATE_RUN
mov [ebx+rndis_dev.RxSize], RNDIS_RX_BUF
; All receive buffers come from one page-aligned block: a 2 KB slice of
; it never crosses a page, so every transfer is a single TRB.
invoke KernelAlloc, RNDIS_RX_BUF * RNDIS_RX_BUFFERS
xor edi, edi
.alloc_loop:
invoke KernelAlloc, RNDIS_RX_BUF
test eax, eax
jz .fail
mov [ebx+rndis_dev.RxArea], eax
xor edi, edi
.slice_loop:
mov [ebx+rndis_dev.RxBufs+edi*4], eax
add eax, RNDIS_RX_BUF
inc edi
cmp edi, RNDIS_RX_BUFFERS
jb .slice_loop
; The pool of transmit slots, all free. It must exist before NetRegDev:
; the stack may transmit as soon as the device is registered.
invoke KernelAlloc, RNDIS_TX_SLOT * RNDIS_TX_MAX_PENDING
test eax, eax
jz .fail
mov [ebx+rndis_dev.TxArea], eax
xor edi, edi
.free_loop:
or [ebx+rndis_dev.TxFree+edi*4], -1
inc edi
cmp edi, RNDIS_TX_MAX_PENDING/32
jb .free_loop
jb .alloc_loop
push ebx
invoke NetRegDev
@@ -1079,9 +952,8 @@ proc rndis_start
mov [ebx+rndis_dev.Registered], 1
DEBUGF 2,"registered as network device %u\n", eax
; the link follows the media indication, or RNDIS_LINK_WAIT expires
cmp [ebx+rndis_dev.LinkKnown], 0
je .link_wait
; An LTE router bridge is up unless the device indicated otherwise
; before we finished initializing.
cmp [ebx+rndis_dev.LinkDown], 0
jne @f
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
@@ -1089,40 +961,18 @@ proc rndis_start
push ebx
invoke NetLinkChanged
pop ebx
jmp .link_done
.link_wait:
invoke TimerHS, RNDIS_LINK_WAIT, 0, rndis_link_timer, ebx
mov [ebx+rndis_dev.LinkTimer], eax
test eax, eax
jnz .link_done
DEBUGF 2,"no timer for the link, assuming it is up\n"
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
push ebx
invoke NetLinkChanged
pop ebx
.link_done:
; Arm every receive buffer at once. Callbacks run in the USB thread,
; woken up to 10 ms after the IRQ; with one transfer in flight a device
; sending one frame per transfer is capped at ~1 Mbit/s. Each callback
; re-arms its own buffer at the ring tail, so frame order is kept.
; NB: on EHCI this used to hurt (see usbcdc.asm); if so, use 2 buffers.
xor edi, edi
.arm_loop:
; Arm the first receive. From here on the RX callback ping-pongs between
; the two buffers: it re-arms the pipe with the other buffer BEFORE
; parsing the completed one, so the device streams the next frames while
; the host walks the current buffer. Exactly one transfer is outstanding
; at any time - queueing several short-packet-terminated bulk IN
; transfers breaks the EHCI stack (see the note in usbcdc.asm).
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], \
[ebx+rndis_dev.RxBufs+edi*4], [ebx+rndis_dev.RxSize], \
[ebx+rndis_dev.RxBufs], [ebx+rndis_dev.RxSize], \
rndis_rx_callback, ebx, 1
test eax, eax
jz .armed
inc [ebx+rndis_dev.RxArmed]
inc edi
cmp edi, RNDIS_RX_BUFFERS
jb .arm_loop
.armed:
cmp [ebx+rndis_dev.RxArmed], 0
je .fail
DEBUGF 2,"receive queue depth %u\n", [ebx+rndis_dev.RxArmed]
jz .fail
ret
.fail:
@@ -1133,8 +983,10 @@ proc rndis_start
endp
if DEBUG_STATS
; Print RX/TX counters about once a second while traffic flows.
; cb/fr = transfers/frames received, tx = frames sent.
; Print transfer-rate statistics to the debug board about once a second
; while traffic flows (called from the RX callback). Shows where the
; bottleneck is: cb/fr = USB transfers and frames per interval (RX),
; tx = frames sent per interval (the TCP ACK clock on downloads).
; IN: ebx = device context
proc rndis_stats
@@ -1149,16 +1001,10 @@ proc rndis_stats
jb .done
mov ecx, [ebx+rndis_dev.packets_tx]
sub ecx, [ebx+rndis_dev.StatTxPkts]
DEBUGF 2,"stat %ut: cb %u fr %u B %u tx %u wnd %u txovr %u txerr %u rxdrop %u rxovr %u gap %u retx %u txq %u tail %u rxu %u rxb %u logdrop %u\n", \
DEBUGF 2,"stat %ut: cb %u fr %u B %u tx %u wnd %u txovr %u rxdrop %u\n", \
edx, [ebx+rndis_dev.StatCb], [ebx+rndis_dev.StatFrames], \
[ebx+rndis_dev.StatBytes], ecx, [ebx+rndis_dev.StatWnd], \
[ebx+rndis_dev.packets_tx_ovr], [ebx+rndis_dev.packets_tx_err], \
[ebx+rndis_dev.packets_rx_drop], [ebx+rndis_dev.packets_rx_ovr], \
[ebx+rndis_dev.StatGap], [ebx+rndis_dev.StatRetx], \
[ebx+rndis_dev.StatTxHigh], [ebx+rndis_dev.StatTail], \
[ebx+rndis_dev.StatRxUni], [ebx+rndis_dev.StatRxBc], \
[ebx+rndis_dev.StatLogDrop]
mov [ebx+rndis_dev.StatTxHigh], 0
[ebx+rndis_dev.packets_tx_ovr], [ebx+rndis_dev.packets_rx_drop]
mov [ebx+rndis_dev.StatCb], 0
mov [ebx+rndis_dev.StatFrames], 0
mov [ebx+rndis_dev.StatBytes], 0
@@ -1170,281 +1016,6 @@ proc rndis_stats
pop edx ecx eax
ret
endp
; Log ARP, ICMP, DNS, DHCP and TCP SYN/FIN/RST in both directions, at
; most RNDIS_LOG_BURST lines a second (the rest counted in logdrop).
; Also counts arrivals to our MAC (rxu) and to a group (rxb).
; IN: ebx = device context, esi -> ethernet frame, ecx = frame length,
; edi = 0 received, 1 sent. Destroys everything, the caller saves it.
align 4
rndis_log_frame:
mov ebp, edi ; direction
test ebp, ebp
jnz .classified
test byte [esi], 1 ; group bit of destination MAC
jz .unicast
inc [ebx+rndis_dev.StatRxBc]
jmp .classified
.unicast:
mov eax, [esi]
cmp eax, dword [ebx+rndis_dev.mac]
jne .classified
mov ax, [esi+4]
cmp ax, word [ebx+rndis_dev.mac+4]
jne .classified
inc [ebx+rndis_dev.StatRxUni]
.classified:
; Pick out the frames worth a line; edx -> transport header.
cmp word [esi+12], 0x0608 ; EtherType ARP (BE 08 06)
je .arp
cmp word [esi+12], 0x0008 ; EtherType IPv4
jne .done
cmp ecx, 14+20
jb .done
movzx eax, byte [esi+14]
and eax, 15
shl eax, 2 ; IP header length
cmp eax, 20
jb .done
test word [esi+20], 0xFF1F ; not the first fragment
jnz .done
lea edx, [esi+14+eax]
add eax, 14+8 ; room for an ICMP/UDP header
cmp ecx, eax
jb .done
cmp byte [esi+23], 1 ; ICMP
je .limit
cmp byte [esi+23], 17 ; UDP
je .udp
cmp byte [esi+23], 6 ; TCP
jne .done
add eax, 20-8
cmp ecx, eax
jb .done
test byte [edx+13], 7 ; FIN, SYN or RST
jz .done
jmp .limit
.udp:
mov eax, [edx] ; both ports, big-endian
mov ecx, 2
.udp_port:
cmp ax, 53 shl 8 ; DNS
je .limit
cmp ax, 67 shl 8 ; DHCP server
je .limit
cmp ax, 68 shl 8 ; DHCP client
je .limit
shr eax, 16
dec ecx
jnz .udp_port
jmp .done
.arp:
cmp ecx, 42
jb .done
; Rate limit: RNDIS_LOG_BURST lines per 100 ticks.
.limit:
push edx
invoke GetTimerTicks
pop edx
mov ecx, eax
sub ecx, [ebx+rndis_dev.LogTicks]
cmp ecx, 100
jb @f
mov [ebx+rndis_dev.LogTicks], eax
mov [ebx+rndis_dev.LogCount], 0
@@:
cmp [ebx+rndis_dev.LogCount], RNDIS_LOG_BURST
jb @f
inc [ebx+rndis_dev.StatLogDrop]
ret
@@:
inc [ebx+rndis_dev.LogCount]
; eax = ticks, ebp = direction, esi -> frame, edx -> transport header
cmp word [esi+12], 0x0608
je .print_arp
cmp byte [esi+23], 1
je .print_icmp
cmp byte [esi+23], 17
je .print_udp
;--------------------------------- TCP
push ebp ; direction, tested below
movzx ecx, word [esi+16]
xchg cl, ch ; IP total length
movzx ebx, byte [esi+14]
and ebx, 15
shl ebx, 2
sub ecx, ebx ; minus the IP header
movzx ebx, byte [edx+12]
shr ebx, 4
shl ebx, 2
sub ecx, ebx ; minus the TCP header
jns @f
xor ecx, ecx
@@:
mov ebp, ecx ; payload length
movzx ecx, word [edx]
xchg cl, ch ; source port
mov di, [edx+2]
rol di, 8
movzx edi, di ; destination port
movzx ebx, word [edx+14]
xchg bl, bh ; window (unscaled)
cmp dword [esp], 0
jne .tcp_out
DEBUGF 2,"t%u in TCP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u flags %x win %u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, \
[edx+13]:2, ebx, ebp
pop eax
ret
.tcp_out:
DEBUGF 2,"t%u out TCP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u flags %x win %u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, \
[edx+13]:2, ebx, ebp
pop eax
ret
;--------------------------------- UDP (DNS, DHCP)
.print_udp:
movzx ecx, word [edx]
xchg cl, ch ; source port
mov di, [edx+2]
rol di, 8
movzx edi, di ; destination port
movzx ebx, word [edx+4]
xchg bl, bh
sub ebx, 8 ; payload length
test ebp, ebp
jnz .udp_out
DEBUGF 2,"t%u in UDP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, ebx
ret
.udp_out:
DEBUGF 2,"t%u out UDP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, ebx
ret
;--------------------------------- ICMP
.print_icmp:
test ebp, ebp
jnz .icmp_out
DEBUGF 2,"t%u in ICMP %u.%u.%u.%u > %u.%u.%u.%u type %u code %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, \
[edx]:1, [edx+1]:1
ret
.icmp_out:
DEBUGF 2,"t%u out ICMP %u.%u.%u.%u > %u.%u.%u.%u type %u code %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, \
[edx]:1, [edx+1]:1
ret
;--------------------------------- ARP
; a request names who asks; a reply names the MAC, which tells a stale
; entry (the phone's address changed) from a missing one
.print_arp:
cmp byte [esi+21], 2 ; opcode: 1 request, 2 reply
je .arp_reply
test ebp, ebp
jnz .arp_req_out
DEBUGF 2,"t%u in ARP who-has %u.%u.%u.%u tell %u.%u.%u.%u\n", eax, \
[esi+38]:1, [esi+39]:1, [esi+40]:1, [esi+41]:1, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1
ret
.arp_req_out:
DEBUGF 2,"t%u out ARP who-has %u.%u.%u.%u tell %u.%u.%u.%u\n", eax, \
[esi+38]:1, [esi+39]:1, [esi+40]:1, [esi+41]:1, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1
ret
.arp_reply:
test ebp, ebp
jnz .arp_rep_out
DEBUGF 2,"t%u in ARP %u.%u.%u.%u is-at %x-%x-%x-%x-%x-%x\n", eax, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1, \
[esi+22]:2, [esi+23]:2, [esi+24]:2, [esi+25]:2, [esi+26]:2, [esi+27]:2
ret
.arp_rep_out:
DEBUGF 2,"t%u out ARP %u.%u.%u.%u is-at %x-%x-%x-%x-%x-%x\n", eax, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1, \
[esi+22]:2, [esi+23]:2, [esi+24]:2, [esi+25]:2, [esi+26]:2, [esi+27]:2
.done:
ret
; Track incoming TCP sequence numbers per flow: gap counts segments
; past a hole, retx segments with data already received (cumulative).
; IN: ebx = device context, esi -> ethernet frame, ecx = frame length
; Destroys everything but ebx and ebp.
proc rndis_track_tcp
cmp ecx, 54 ; eth+min IP+min TCP
jb .done
cmp word [esi+12], 0x0008 ; EtherType IPv4
jne .done
cmp byte [esi+23], 6 ; IP protocol TCP
jne .done
movzx edx, byte [esi+14]
and edx, 15
shl edx, 2 ; IP header length
movzx eax, word [esi+16]
xchg al, ah ; IP total length
sub eax, edx
jbe .done
lea edi, [esi+14+edx] ; TCP header
movzx edx, byte [edi+12]
shr edx, 4
shl edx, 2 ; TCP header length
sub eax, edx ; payload length
jbe .done ; pure ACK: no sequence space
mov ecx, eax
mov edx, [esi+26] ; source IP
xor edx, [edi] ; ^ source/destination ports
mov eax, [edi+4]
bswap eax ; sequence number
lea esi, [ebx+rndis_dev.Flows]
mov edi, RNDIS_FLOWS
.find:
cmp [esi], edx
je .found
add esi, 8
dec edi
jnz .find
; a new flow: take over the oldest slot
mov edi, [ebx+rndis_dev.FlowNext]
inc [ebx+rndis_dev.FlowNext]
and edi, RNDIS_FLOWS-1
lea esi, [ebx+rndis_dev.Flows+edi*8]
mov [esi], edx
jmp .advance
.found:
mov edx, eax
sub edx, [esi+4] ; seq - expected
jz .advance
js .old
inc [ebx+rndis_dev.StatGap]
jmp .advance
.old:
inc [ebx+rndis_dev.StatRetx]
lea edx, [eax+ecx]
sub edx, [esi+4]
jle .done ; nothing new in it
.advance:
add eax, ecx
mov [esi+4], eax
.done:
ret
endp
end if ; DEBUG_STATS
@@ -1460,12 +1031,11 @@ proc rndis_rx_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword, .bu
locals
msg_off dd ? ; offset of the current message
rearmed dd ? ; the other buffer is already armed
endl
mov ebx, [.calldata]
DEBUGF 1,"RX cb st %u len %u\n", [.status], [.length]
; this transfer has left the queue, whatever its outcome
dec [ebx+rndis_dev.RxArmed]
cmp [ebx+rndis_dev.Dead], 0
jne .ret
cmp [.status], USB_STATUS_CLOSED
@@ -1489,13 +1059,28 @@ end if
jae .rx_dead
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], [.buffer], \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jz .check_dry
inc [ebx+rndis_dev.RxArmed]
jmp .ret
.status_ok:
mov [ebx+rndis_dev.RxErrRun], 0 ; the error run is broken
; Re-arm the pipe with the other buffer BEFORE parsing this one: the
; device streams the next frames while the host walks the current
; buffer. The completed transfer is already off the queue, so still at
; most one transfer is outstanding (a hard requirement of the EHCI
; stack, see the note in usbcdc.asm).
mov [rearmed], 0
mov eax, [ebx+rndis_dev.RxBufs]
cmp eax, [.buffer]
jne @f
mov eax, [ebx+rndis_dev.RxBufs+4]
@@:
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], eax, \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jz @f
mov [rearmed], 1
@@:
mov [msg_off], 0
.msg_loop:
@@ -1566,30 +1151,9 @@ end if
if DEBUG_STATS
inc [ebx+rndis_dev.StatFrames]
add [ebx+rndis_dev.StatBytes], ecx
pushad
lea esi, [edi+NET_BUFF.data]
xor edi, edi
call rndis_log_frame
popad
pushad
lea esi, [edi+NET_BUFF.data]
call rndis_track_tcp
popad
end if
DEBUGF 1,"->host frame %u bytes\n", [edi+NET_BUFF.length]
; Frames arrive, so the link is up despite MEDIA_DISCONNECT (a phone
; tethering at cold boot never sends MEDIA_CONNECT).
cmp [ebx+rndis_dev.LinkDown], 0
je .link_ok
mov [ebx+rndis_dev.LinkDown], 0
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
DEBUGF 2,"frames arrive while the link is down, taking it as up\n"
pushad
invoke NetLinkChanged
popad
.link_ok:
; hand the buffer to the network stack. EthInput consumes
; [esp] = buffer and returns to the address at [esp+4].
push ebp ebx esi
@@ -1604,31 +1168,19 @@ end if
inc [ebx+rndis_dev.packets_rx_drop]
jmp .msg_loop
; put this buffer back at the tail of the queue
; End of processing. If arming the other buffer failed at entry, fall
; back to re-submitting the just-parsed buffer so receive does not stop.
.rearm:
if DEBUG_STATS
; bytes of the transfer not turned into frames; must stay 0
mov eax, [.length]
sub eax, [msg_off]
jbe @f
add [ebx+rndis_dev.StatTail], eax
@@:
call rndis_stats
end if
cmp [rearmed], 0
jne .ret
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], [.buffer], \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jz .rearm_failed
inc [ebx+rndis_dev.RxArmed]
ret
.rearm_failed:
jnz .ret
DEBUGF 2,"failed to re-arm RX\n"
; Losing one buffer of the queue is survivable as long as something is
; still outstanding; if nothing is, receive has stopped for good.
.check_dry:
cmp [ebx+rndis_dev.RxArmed], 0
jne .ret
DEBUGF 2,"receive queue ran dry, receive stopped\n"
.ret:
ret
@@ -1636,11 +1188,7 @@ end if
DEBUGF 2,"bulk IN stalled, receive stopped\n"
ret
; Every buffer still in the queue arrives here in turn; report the death
; of the pipe only once.
.rx_dead:
cmp [ebx+rndis_dev.state], ETH_LINK_DOWN
je .ret
DEBUGF 2,"RX stopped after repeated transfer errors\n"
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
push ebx
@@ -1665,50 +1213,7 @@ proc rndis_tx_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword, .bu
DEBUGF 1,"TX error %u\n", [.status]
@@:
mov eax, [.calldata]
call rndis_tx_release
ret
endp
; Take a free transmit slot.
; IN: ebx = device context. OUT: eax = slot, 0 if none. Destroys ecx, edx.
; The stack transmits on its own thread while completions run on the USB
; thread, possibly on another CPU, hence the locked bit operations.
if RNDIS_TX_SLOT <> 2048
err "rndis_tx_claim/rndis_tx_release shift by 11 for 2 KB slots"
end if
proc rndis_tx_claim
xor edx, edx
.scan:
mov eax, [ebx+rndis_dev.TxFree+edx*4]
test eax, eax
jz .next
bsf ecx, eax
lock btr [ebx+rndis_dev.TxFree+edx*4], ecx
jnc .scan ; taken meanwhile: look again
shl edx, 5
add edx, ecx ; slot number
shl edx, 11
mov eax, [ebx+rndis_dev.TxArea]
add eax, edx
ret
.next:
inc edx
cmp edx, RNDIS_TX_MAX_PENDING/32
jb .scan
xor eax, eax
ret
endp
; Give a transmit slot back.
; IN: ebx = device context, eax = slot. Destroys eax.
proc rndis_tx_release
sub eax, [ebx+rndis_dev.TxArea]
shr eax, 11 ; slot number
lock bts [ebx+rndis_dev.TxFree], eax
invoke Kfree
ret
endp
@@ -1722,8 +1227,8 @@ endp
;; ;;
;; The frame is prepended with a 44-byte REMOTE_NDIS_PACKET_MSG. ;;
;; If the transfer would be a multiple of the endpoint packet ;;
;; size, one extra zero byte is sent so that the transfer ends in ;;
;; a short packet; it is counted in MessageLength (QEMU needs it). ;;
;; size, one extra zero byte is sent (not counted in the message ;;
;; length) so that the transfer ends in a short packet. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1746,22 +1251,13 @@ endl
je .drop
mov ecx, [esi+NET_BUFF.length]
cmp ecx, 14
jb .badlen
jb .err
cmp ecx, ETH_FRAME_MAX
ja .badlen
ja .err
DEBUGF 1,"host-> frame %u bytes\n", ecx
; limit the number of messages in flight
lock inc [ebx+rndis_dev.TxPending]
if DEBUG_STATS
; remember how deep the queue actually gets: the margin left before
; frames start being refused
mov eax, [ebx+rndis_dev.TxPending]
cmp eax, [ebx+rndis_dev.StatTxHigh]
jbe @f
mov [ebx+rndis_dev.StatTxHigh], eax
@@:
end if
cmp [ebx+rndis_dev.TxPending], RNDIS_TX_MAX_PENDING
ja .overrun
@@ -1780,7 +1276,6 @@ end if
test eax, edx
jnz @f
inc eax
mov [msglen], eax ; the pad byte is part of it
@@:
mov [xferlen], eax
; never exceed what the device declared it can receive
@@ -1790,8 +1285,9 @@ end if
cmp eax, edx
ja .toobig
@@:
; take a slot for [context dword][message]
call rndis_tx_claim
; allocate [context dword][message]
add eax, 4
invoke Kmalloc
test eax, eax
jz .no_memory
mov [alloc], eax
@@ -1825,15 +1321,6 @@ end if
rep stosb
if DEBUG_STATS
; stats: spell out the control frames we send
pushad
mov esi, [alloc]
add esi, 4+RNDIS_HDR_SIZE ; frame start
mov ecx, edx ; frame length
movi edi, 1
call rndis_log_frame
popad
; stats: remember the TCP receive window we advertise (rwnd health on
; downloads; parsed from the outgoing frame: eth+IPv4+TCP)
cmp edx, 54 ; eth+min IP+min TCP
@@ -1872,7 +1359,7 @@ end if
.submit_failed:
mov eax, [alloc]
call rndis_tx_release
invoke Kfree
lock dec [ebx+rndis_dev.TxPending]
jmp .err
@@ -1889,16 +1376,6 @@ end if
inc [ebx+rndis_dev.packets_tx_ovr]
jmp .drop
; A frame the stack handed down does not fit an ethernet frame at all.
; Report the first few: the length says at once whether the stack built
; an oversized segment (t_maxseg not clamped to our mtu, which shows up
; as an endlessly retransmitting connection) or something else.
.badlen:
cmp [ebx+rndis_dev.LenErrNoted], 4
jae .err
inc [ebx+rndis_dev.LenErrNoted]
DEBUGF 2,"refusing a frame of %u bytes\n", ecx
.err:
inc [ebx+rndis_dev.packets_tx_err]
.drop:
@@ -1933,11 +1410,6 @@ proc DeviceDisconnected stdcall uses ebx esi edi, .devdata:dword
mov ebx, [.devdata]
DEBUGF 2,"device disconnected\n"
mov [ebx+rndis_dev.Dead], 1
mov eax, [ebx+rndis_dev.LinkTimer]
test eax, eax
jz @f
invoke CancelTimerHS, eax ; must not fire on a freed context
@@:
cmp [ebx+rndis_dev.Registered], 0
je @f
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
@@ -1949,17 +1421,16 @@ proc DeviceDisconnected stdcall uses ebx esi edi, .devdata:dword
pop ebx
mov [ebx+rndis_dev.Registered], 0
@@:
; every transfer callback has run by now, so no slot or buffer is in use
mov eax, [ebx+rndis_dev.RxArea]
test eax, eax
jz @f
invoke KernelFree, eax
@@:
mov eax, [ebx+rndis_dev.TxArea]
xor esi, esi
.free_loop:
mov eax, [ebx+rndis_dev.RxBufs+esi*4]
test eax, eax
jz @f
invoke KernelFree, eax
@@:
inc esi
cmp esi, RNDIS_RX_BUFFERS
jb .free_loop
mov eax, ebx
invoke Kfree
ret
@@ -2009,6 +1480,5 @@ end data
align 4
parse_scratch rndis_parse
random_mac rb 6 ; host address used instead of a made-up one
include_debug_strings
+1 -1
View File
@@ -2628,7 +2628,7 @@ modify_pce:
iglobal
cpustring db 'CPU',0
cpustring db 'TASKMAN',0
endg
uglobal
+1 -1
View File
@@ -1,5 +1,5 @@
[rbmenu]
Диспетчер процессов=cpu
Диспетчер задач=taskman
Управление темой=skincfg
Настройка фона=/sys/media/palitra
Настройка устройств=setup
+42
View File
@@ -0,0 +1,42 @@
## TASKMAN - Task Manager
Replaces CPU and SYSMON. Starts from the menu, the system panel or by
`Ctrl+Alt+Del`.
* **Processes** - threads with their CPU and memory. `Details` opens
TINFO, `End` ends the thread, `Run...` starts a program. System threads
(OS, @...) are shown with `System`.
* **Performance** - CPU, temperature, memory and network: the values on
the left, the history as a graph on the right. Temperature comes from
the CPU sensor (Intel, AMD, Hygon, VIA, Zhaoxin) or else from a board
monitoring chip (Winbond, ITE, ABIT uGuru); without either there is no
temperature graph. `CPUID` starts CPUID.
* **Disks** - partitions with label, size and, for memory disks, the
space used. `Open` shows the disk in Eolite.
* **Drivers** - the drivers the kernel has loaded. `Load...` starts
LOADDRV.
* **Autorun** - the programs of `/sys/settings/autorun.dat`. A program
turned off has `#` before it and is shown pale; `Turn on/off` adds or
removes the `#` in the file.
Lists sort by a click on a column header; a second click reverses the
order.
### Keys
| Key | Action |
|---|---|
| `Tab` | Next tab |
| `Esc` | Close |
| `Up`, `Down`, `PgUp`, `PgDn`, `Home`, `End` | Move in a list |
| `Enter` | Processes: Details; Disks: Open |
| `Del` | Processes: End |
| `Space` | Autorun: turn the program on or off |
| Right click on a row | Processes: Details; Disks: Open |
| Mouse wheel | Scroll a list |
### History
* 2026-10 - First version. Replaces CPU (processes) and SYSMON (system
monitor); adds the disks, drivers and autorun tabs and the CPU
temperature sensors; the board chips come from GMON.
+4
View File
@@ -0,0 +1,4 @@
if tup.getconfig("NO_FASM") ~= "" then return end
lang = (tup.getconfig("LANG") == "") and "en_US" or tup.getconfig("LANG")
tup.rule("taskman.asm", "fasm -dlang=" .. lang .. " %f %o " .. tup.getconfig("KPACK_CMD"), "taskman")
+423
View File
@@ -0,0 +1,423 @@
; Drawing and formatting helpers; they keep all registers unless noted.
macro __mov reg, a, b { ; mcall loads small constants shorter
if (~a eq) & (~b eq)
mpack reg, a, b
else if (~a eq) & (b eq)
movi reg, a
end if
}
; An item of a bar: a button with its color, a checkbox with its state or
; a gap
struct BAR
kind db ?
text dd ? ; the caption
arg dd ? ; -> the color, -> the state as a byte
ends
BAR_END = 0
BAR_BUTTON = 1
BAR_CHECK = 2
BAR_GAP = 3
; ebx = x shl 16 + y, ecx = color, edx -> string in UTF-8
draw_text:
pusha
or ecx, TEXT_UTF8
mcall SF_DRAW_TEXT
popa
ret
; eax -> string; out: eax = width in pixels
text_width:
push ecx esi
mov esi, eax
xor ecx, ecx
.next:
lodsb
test al, al
jz .done
call utf8_more
je .next
add ecx, 8
jmp .next
.done:
xchg eax, ecx
pop esi ecx
ret
; eax -> caption; out: eax = width of the button
button_width:
call text_width
add eax, BTN_PAD*2
ret
; Frame in the border color filled with edx. ebx = x shl 16 + w, ecx = y shl 16 + h
frame_rect:
pusha
push edx
mcall SF_DRAW_RECT, , , [sc.work_graph]
pop edx
add ebx, 1 shl 16 - 2
add ecx, 1 shl 16 - 2
mcall SF_DRAW_RECT
popa
ret
; eax = icon, ebx = x shl 16 + y, edx = the background for its white
draw_icon:
pusha
mov esi, [icons]
test esi, esi
jz .done
imul eax, ICON*ICON*4
add esi, eax
mov edi, icon_buf
mov ecx, ICON*ICON
@@:
lodsd
cmp eax, -1
jne .pixel
mov eax, edx
.pixel:
stosd
loop @b
mov edx, ebx
mcall SF_PUT_IMAGE_EXT, icon_buf, (ICON shl 16) + ICON, , 32, 0, 0
.done:
popa
ret
; eax, edx = colors; out: eax = the one half way between them, edx changed
mix:
and eax, 0xFEFEFE
and edx, 0xFEFEFE
add eax, edx
shr eax, 1
ret
; eax -> caption, edx = id, esi = color
bar_button:
pusha
mov edi, eax
call button_width
call bar_place
dec ebx ; the kernel draws one pixel more
dec ecx
mcall SF_DEFINE_BUTTON
add ebx, BTN_PAD shl 16
mov eax, [sc.work_button_text] ; then bar_text
; The end of the bar items: edi -> caption, eax = color, ebx = x shl 16,
; ecx = y shl 16 of the item
bar_text:
shr ecx, 16
add ecx, (BTN_H - TEXT_H)/2
mov bx, cx
xchg eax, ecx
mov edx, edi
call draw_text
popa
ret
; eax -> caption, edx = id, ebp = checked
bar_checkbox:
pusha
mov edi, eax
call button_width ; the box, PAD, the caption: 2 left
call bar_place
add [bar_left], 2*PAD + CHECK - 2*BTN_PAD ; then more room
or edx, BT_HIDE + BT_NOFRAME
mcall SF_DEFINE_BUTTON
mov bx, CHECK
add ecx, ((BTN_H - CHECK)/2) shl 16
mov cx, CHECK
mov [cb.left_s], ebx ; the box of box_lib, the caption
mov [cb.top_s], ecx ; as the other ones
mov eax, [sc.work_light]
mov [cb.color], eax
mov eax, [sc.work_button] ; the frame and the check
mov [cb.border_color], eax
lea eax, [ebp + ebp] ; checked
mov [cb.flags], eax
push cb
call [check_box_draw2]
add ebx, (CHECK + PAD) shl 16
sub ecx, ((BTN_H - CHECK)/2) shl 16
mov eax, [sc.work_text]
jmp bar_text
; Place in the bottom bar: from the left end, or from the right one for ids
; with bit 3. eax = width, edx = id; out: ebx = x shl 16 + w, ecx = y shl 16 + h
bar_place:
lea ebx, [eax + PAD]
test dl, 8
jnz .right
xchg ebx, [bar_left]
add [bar_left], ebx
jmp @f
.right:
sub [bar_right], ebx
mov ebx, [bar_right]
@@:
shl ebx, 16
mov bx, ax
mov ecx, [bar_y]
shl ecx, 16
mov cx, BTN_H
ret
; The items of a bar from the left, esi -> them; the buttons and the
; checkboxes get the ids from BTN_OWN on. Changes all registers.
bar_draw:
mov edx, BTN_OWN
.item:
mov bl, [esi + BAR.kind]
mov eax, [esi + BAR.text]
mov ecx, [esi + BAR.arg]
add esi, sizeof.BAR
dec bl
js .done
jz .button
dec bl
jz .check
add [bar_left], PAD ; a gap
jmp .item
.done:
ret
.button:
push esi
mov esi, [ecx]
call bar_button
pop esi
inc edx
jmp .item
.check:
movzx ebp, byte [ecx]
call bar_checkbox
inc edx
jmp .item
;-------------------------------------------------------------------------------
; Formatting: write to edi and leave it at the terminating zero.
; eax = number; printed as 8 hex digits
fmt_hex:
pusha
mov ebx, 16
mov esi, 8
jmp fmt_num
; eax = number
fmt_uint:
pusha
mov ebx, 10
xor esi, esi
fmt_num: ; ebx = base, esi = the fewest digits
xor ecx, ecx
@@:
xor edx, edx
div ebx
push edx
inc ecx
test eax, eax
jnz @b
cmp ecx, esi
jb @b
@@:
pop eax
cmp al, 10 ; 0..15 to '0'..'9', 'A'..'F'
sbb al, 0x69
das
stosb
loop @b
mov [edi], cl
mov [esp + PUSHA_EDI], edi
popa
ret
; al = a byte of UTF-8; out: ZF = 1 if it goes on with a character
utf8_more:
push eax
and al, 0xC0
cmp al, 0x80
pop eax
ret
; edx -> string; copies up to (ebx - eax) / 8 characters, TEXT_CHARS at
; most, and keeps edi
fmt_clip:
pusha
mov esi, edx
sub ebx, eax
shr ebx, 3
cmp ebx, TEXT_CHARS
jb @f
mov ebx, TEXT_CHARS
@@:
lodsb
test al, al
jz @f
call utf8_more
je .put
dec ebx
js @f
.put:
stosb
cmp edi, text_buf + TEXT_CHARS*4
jb @b
@@:
mov byte [edi], 0
popa
ret
; esi -> string
fmt_str:
push eax esi
@@:
lodsb
stosb
test al, al
jnz @b
dec edi
pop esi eax
ret
; eax -> string
fmt_unit:
xchg esi, eax
call fmt_str
xchg esi, eax
ret
; eax = KB; printed as "123 KB", "1.5 MB", "12.3 GB" etc.
fmt_size:
pusha
mov ebx, 1024
mov esi, sz_units
jmp fmt_scaled
; eax = bits a second; printed as "500 bit/s", "1.5 Mbit/s" etc.
fmt_rate:
pusha
mov ebx, 1000
mov esi, sz_bits ; then fmt_scaled
; ebx = the step of the units, esi -> the units, each ended with 0: the
; number in the largest one it fills, small ones with a decimal but .0
fmt_scaled:
xor ecx, ecx
xor edx, edx
@@:
cmp eax, ebx
jb @f
xor edx, edx
div ebx
inc ecx
jmp @b
@@:
call fmt_uint
cmp eax, 100
jae .unit
imul eax, edx, 10
xor edx, edx
div ebx
test eax, eax
jz .unit
mov byte [edi], "."
inc edi
call fmt_uint
.unit:
dec ecx
js @f
.skip:
lodsb
test al, al
jnz .skip
jmp .unit
@@:
call fmt_str
mov [esp + PUSHA_EDI], edi
popa
ret
; eax = seconds; printed as "h:mm:ss"
fmt_time:
pusha
xor edx, edx
mov ecx, 3600
div ecx
call fmt_uint
xchg eax, edx
mov cl, 60
div cl
movzx edx, ah
call .two
xchg eax, edx
call .two
mov [esp + PUSHA_EDI], edi
popa
ret
.two: ; eax = 0..99
aam
add ax, "00"
xchg al, ah
shl eax, 8
mov al, ":"
stosd
dec edi
ret
; esi -> path: the program without parameters
run_plain:
xor edx, edx ; then run_app
; esi -> path, edx -> parameters or 0
run_app:
pusha
mov [app_info.Flags], edx
mov [app_info.FileName], esi
mcall SF_FILE, app_info
popa
ret
+188
View File
@@ -0,0 +1,188 @@
; The monitoring chips of older boards, ported from GMON: Winbond W836xx and
; ITE IT87xx at ports 0x295/0x296, ABIT uGuru at 0xE0/0xE4. Their first
; temperature stands in for a CPU without a sensor.
HWM_PORT = 0x295 ; index, data at + 1
SIO_PORT = 0x2E ; Super I/O config, data at + 1
UGURU_CMD = 0xE0
UGURU_DATA = 0xE4
WB_BANK = 0x4E ; the registers of Winbond: the bank,
WB_VENDOR = 0x4F ; the vendor, 0x80 in WB_BANK: its
WB_ID = 0x5CA3 ; high byte
WB_TEMP = 0x50 ; in bank 1
SIO_CHIP_ID = 0x20 ; of the Super I/O, then of ITE
ITE_ID = 0x87 ; the high byte
ITE_TEMP = 0x29
UGURU_TEMP = 0x2100 ; bank, sensor
; The chip of the board: hwm_read; out: eax = it, 0 if none: the ports
; given back
hwm_init:
mcall SF_SET_PORTS, 0, HWM_PORT, HWM_PORT + 1
test eax, eax
jnz .uguru
call wb_probe
je .found
mcall SF_SET_PORTS, 0, SIO_PORT, SIO_PORT + 1
test eax, eax
jnz .uguru
call ite_probe
je .found
.uguru:
mcall SF_SET_PORTS, 0, UGURU_CMD, UGURU_DATA
test eax, eax
jnz .none
in al, UGURU_CMD
test al, al
jz @f
cmp al, 0xAC
jne .none
@@:
in al, UGURU_DATA
test al, al
jz @f
cmp al, 8
jne .none
@@:
mov eax, uguru_read
.found:
mov [hwm_read], eax
ret
.none:
mcall SF_SET_PORTS, 1, HWM_PORT, HWM_PORT + 1
mcall SF_SET_PORTS, 1, UGURU_CMD, UGURU_DATA
xor eax, eax
ret
; al = bank (or 0x80: the high byte of the vendor), ah = register; out: eax
wb_get:
push eax
mov ah, WB_BANK
call hwm_index
pop eax
out dx, al ; then hwm_get
; ah = register; out: eax
hwm_get:
call hwm_index
in al, dx
movzx eax, al
ret
hwm_index: ; out: dx = the data port
mov dx, HWM_PORT
mov al, ah
out dx, al
inc edx
ret
; out: ZF = 1 and eax = the read procedure if found
wb_probe:
mov ax, WB_VENDOR shl 8 ; a byte at a time
call wb_get
xchg ebx, eax
mov ax, WB_VENDOR shl 8 + 0x80
call wb_get
mov bh, al
cmp bx, WB_ID
jne .done
mov eax, wb_read
cmp eax, eax
.done:
ret
; out: ZF = 1 and eax = the read procedure if found
ite_probe:
mov eax, 0x55550187 ; MB PnP mode
mov ecx, 4
@@:
out SIO_PORT, al
shr eax, 8
loop @b
mov al, SIO_CHIP_ID
out SIO_PORT, al
in al, SIO_PORT + 1
xchg eax, esi
mov al, 2 ; leave the mode
out SIO_PORT, al
out SIO_PORT + 1, al
mcall SF_SET_PORTS, 1, SIO_PORT, SIO_PORT + 1
xchg eax, esi
cmp al, ITE_ID
mov eax, ite_read
ret
; out: eax = the first temperature
wb_read:
mov ax, WB_TEMP shl 8 + 1
jmp wb_get
ite_read:
mov ah, ITE_TEMP
jmp hwm_get
uguru_read:
mov ax, UGURU_TEMP ; then uguru_sensor
; ax = sensor; out: eax
uguru_sensor:
push ebx
mov ebx, eax
mov al, ah ; the bank
out UGURU_DATA, al
mov ah, 8
call .wait
mov al, bl
out UGURU_CMD, al
mov ah, 1
call .wait
in al, UGURU_CMD
movzx ebx, al
mov ecx, 1000 ; back to the ready state
.ready:
in al, UGURU_DATA
cmp al, 8
je .done
xor eax, eax
out UGURU_DATA, al
mov ah, 9
call .wait
loop .ready
.done:
xchg eax, ebx
pop ebx
ret
.wait: ; for ah at the data port
push ecx
mov ecx, 1000
@@:
in al, UGURU_DATA
cmp al, ah
loopne @b
pop ecx
ret
hwm_read dd nothing ; the procedure of the chip
uglobal
align 4
hwm_temp dd ? ; degrees
endg
+684
View File
@@ -0,0 +1,684 @@
; List of a tab: rows in columns with a header, a selected row and a scrollbar on
; the panel. The columns go from the left, one of them, width 0, takes what
; the others leave. A click on a row selects it, then calls the trigger of
; that button; a key of the hotkeys calls its trigger. `list` is the list of
; the open tab; a closed tab keeps it in its descriptor.
struct LIST
cols dd ? ; the columns
cols_end dd ?
sort dd ? ; the sorted column
left dd ? ; the triggers: a left click,
right dd ? ; a right click,
keys dd ? ; -> HOTKEYs, ended by 0
dim dd ? ; a byte in the row: not 0, it is pale; 0: none
count dd ?
sel dd ?
first dd ?
key dd ? ; the first dword of the selected row
desc dd ? ; 1: the sorted column goes down
view dd ? ; -> the pointers to the rows
ends
struct HOTKEY
key db ?
trigger dw ?
ends
struct COL
caption dd ?
field db ? ; offset in the row
kind db ?
width db ? ; 0: what the others leave
ends
; The list: the header, the rows and the scrollbar.
list_draw:
mov ebx, [area_w] ; what the others leave to the widest
sub ebx, 1 + SB_W
mov esi, [list.cols]
@@:
movzx edx, byte [esi + COL.width]
sub ebx, edx
add esi, sizeof.COL
cmp esi, [list.cols_end]
jb @b
mov [col_rest], ebx
mov eax, [area_h] ; the rows that fit
sub eax, 2 + HDR_H + 1
xor edx, edx
mov ecx, ROW_H
div ecx
mov [rows], eax
mov ebx, PAD shl 16 ; the header and the line under it,
mov bx, word [area_w] ; left of the scrollbar
sub bx, SB_W - 1
mov ecx, (AREA_Y shl 16) + HDR_H + 2
mov edx, [mid]
call frame_rect
xor eax, eax
mov ebp, AREA_Y + 1 + (HDR_H - TEXT_H)/2
call list_line ; then list_rows
; The rows and the scrollbar only, for new data.
list_rows:
call list_clamp
mov edi, [list.first]
mov ecx, ((AREA_Y + HDR_H + 2) shl 16) + ROW_H
mov ebp, [rows]
.row:
mov ebx, (PAD + 1) shl 16
mov bx, word [area_w]
sub bx, 1 + SB_W
mov edx, [sc.work_light]
mov esi, [sc.work_text]
cmp edi, [list.sel]
jne @f
cmp edi, [list.count] ; a row there
jae @f
mov edx, [sc.work_button]
mov esi, [sc.work_button_text]
@@:
mcall SF_DRAW_RECT
cmp edi, [list.count]
jae @f
pusha
mov eax, [list.view]
mov eax, [eax + edi*4]
mov ebp, ecx
shr ebp, 16
add ebp, (ROW_H - TEXT_H)/2
mov ecx, esi
mov esi, [list.dim] ; pale: half way to the background
test esi, esi
jz .line
cmp byte [eax + esi], 0
je .line
xchg eax, ecx
call mix
xchg eax, ecx
.line:
call list_line
popa
@@:
add ecx, ROW_H shl 16
inc edi
dec ebp
jg .row ; then sb_draw
; The scrollbar of box_lib at the right of the list, as high as it
sb_draw:
call sb_set
push sb
call [scrollbar_v_draw]
ret
; One line of the list at y = ebp: the captions if eax = 0, lines between
; them, the sorted one with an arrow, otherwise the cells of row eax in color
; ecx on background edx. Left or right as col_left says.
list_line:
pusha
mov ebx, PAD + 1 ; the left of the column
mov esi, [list.cols]
.col:
pusha
movzx edx, byte [esi + COL.field]
add edx, eax
test eax, eax
jnz .cell
mov edx, [esi + COL.caption]
mov ecx, [sc.work_text]
jmp .draw
.cell:
call cell_text
.draw:
call col_width ; the text in the cell
lea eax, [ebx + eax - CELL_PAD]
xchg eax, ebx
add eax, CELL_PAD
mov edi, text_buf
call fmt_clip
mov edx, edi
call col_left
jc @f
mov eax, edx
call text_width
neg eax
add eax, ebx
@@:
shl eax, 16
mov ax, bp
xchg eax, ebx
call draw_text
cmp dword [esp + PUSHA_EAX], 0 ; the captions
jne .picture
cmp esi, [list.cols] ; a line left of the column, but the
je @f ; first one at the frame
push ebx
mov ebx, [esp + 4 + PUSHA_EBX] ; the left
shl ebx, 16
inc ebx
mov ecx, ((AREA_Y + 1) shl 16) + HDR_H
mcall SF_DRAW_RECT, , , [sc.work_graph]
pop ebx
@@:
cmp esi, [list.sort]
jne .next
mov edi, [list.desc]
sub ebx, 5 shl 16 ; 2 pixels before the caption on the
call col_left ; right, after the one on the left
jnc @f
mov eax, text_buf
call text_width
add eax, 11
shl eax, 16
add ebx, eax
@@:
call draw_arrow
jmp .next
.picture: ; a bar or an icon in a row
movzx edx, byte [esi + COL.field]
add edx, [esp + PUSHA_EAX] ; the row
mov edx, [edx]
call col_width
cmp byte [esi + COL.kind], KIND_BAR
jb .next
ja .icon
mov ebx, [esp + PUSHA_EBX] ; the bar across the cell
add ebx, CELL_PAD
shl ebx, 16
sub eax, 2*CELL_PAD
mov bx, ax
movzx ecx, bp
shl ecx, 16
mov cx, TEXT_H
xchg eax, edx
mov edx, [sc.work_light]
call frame_rect
cmp eax, -1
je .next
add ebx, 2 shl 16 - 4 ; a light line around it, from the
add ecx, 2 shl 16 - 4 ; selected row too
movzx edx, bx
mul edx
shr eax, 10
mov bx, ax
mcall SF_DRAW_RECT, , , [sc.work_button] ; as the graphs
jmp .next
.icon:
sub eax, ICON ; in the middle of the cell
shr eax, 1
add eax, [esp + PUSHA_EBX]
shl eax, 16
lea ecx, [ebp - (ICON - TEXT_H)/2]
mov ax, cx
xchg eax, ebx
xchg eax, edx
mov edx, [esp + PUSHA_SIZE + PUSHA_EDX] ; of the first one:
; the background
call draw_icon
.next:
call col_width ; the next column after this one
add [esp + PUSHA_EBX], eax ; its left
popa
add esi, sizeof.COL
cmp esi, [list.cols_end]
jb .col
popa
ret
; esi -> column, edx -> its field in the row; out: edx -> the text of the
; cell, "" for a bar or an icon
cell_text:
cmp byte [esi + COL.kind], KIND_STR
jb .done
mov edx, [edx]
je .done
mov eax, edx
mov edi, text_buf
mov edx, edi
mov byte [edi], 0 ; -1: unknown, nothing to show
cmp eax, -1
je .done
cmp byte [esi + COL.kind], KIND_SIZE
jb .uint
ja .hex
jmp fmt_size
.hex:
cmp byte [esi + COL.kind], KIND_HEX ; a bar or an icon: drawn below
jne .done
jmp fmt_hex
.uint:
call fmt_uint
cmp byte [esi + COL.kind], KIND_PCT
jne .done
mov word [edi], "%"
.done:
ret
; esi -> column; out: CF = 1 if its caption and text go left: text and the
; widest column, but the bar
col_left:
cmp byte [esi + COL.kind], KIND_NUM ; text: CF = 1
jb .done
cmp byte [esi + COL.kind], KIND_BAR ; the bar and icons: CF = 0
jae .done
cmp byte [esi + COL.width], 1 ; the widest: CF = 1
.done:
ret
; esi -> column; out: eax = its width
col_width:
movzx eax, byte [esi + COL.width]
test eax, eax
jnz @f
mov eax, [col_rest]
@@:
ret
; The end of a scan: ebp -> the end of the view.
list_done:
sub ebp, [list.view]
shr ebp, 2
mov [list.count], ebp ; then list_sort
; Sort the view by the sorted column and find the selected row again.
list_sort:
mov ebp, [list.view]
mov ecx, 1 ; insertion sort, stable
.outer:
cmp ecx, [list.count]
jae .select
mov esi, [ebp + ecx*4]
mov ebx, ecx
.inner:
mov edi, [ebp + ebx*4 - 4]
call list_less
jnc .put
mov [ebp + ebx*4], edi
dec ebx
jnz .inner
.put:
mov [ebp + ebx*4], esi
inc ecx
jmp .outer
.select:
mov ecx, [list.count]
mov eax, [list.key]
@@:
dec ecx
js @f
mov esi, [ebp + ecx*4]
cmp [esi], eax
jne @b
mov [list.sel], ecx
@@:
ret
; esi, edi -> rows; out: CF = 1 if esi goes before edi
list_less:
pusha
mov ebx, [list.sort]
movzx ecx, byte [ebx + COL.field]
add esi, ecx
add edi, ecx
cmp [list.desc], 0
je @f
xchg esi, edi
@@:
cmp byte [ebx + COL.kind], KIND_STR
ja .num
jb @f
mov esi, [esi] ; the text by its pointer
mov edi, [edi]
@@:
lodsb ; the letters of either case alike
mov ah, [edi]
inc edi
mov dl, al
or ax, 0x2020
cmp al, ah
jne @f
test dl, dl
jz @f
cmp byte [edi - 1], 0 ; the end of edi, not a blank
jne @b
@@:
popa
ret
.num: ; -1, unknown, before 0
mov eax, [esi]
inc eax
mov edx, [edi]
inc edx
cmp eax, edx
popa
ret
; An arrow up, or down if edi = 1: a triangle of 4 lines, line i of 2*i + 1
; pixels, its tip at x = ebx shr 16, around the middle of the capitals of
; text at y = bx.
draw_arrow:
pusha
lea ecx, [edi*2 + edi + TEXT_H/2 - 2] ; from the top if it
add cx, bx ; goes up, else from the bottom
shl ecx, 16
inc ecx
shl edi, 17 ; the next line: 1 down or up
neg edi
add edi, 1 shl 16
mov bx, 1
mov esi, 4
@@:
mcall SF_DRAW_RECT, , , [sc.work_text]
add ebx, (-1 shl 16) + 2
add ecx, edi
dec esi
jnz @b
popa
ret
; eax = rows the wheel moves, esi = mouse buttons: box_lib moves the list,
; again while held; out: ebp = list.first before, drawn again if it changes
scroll_mouse:
mov ebp, [list.first]
add [list.first], eax
call sb_set
xor eax, eax
mov edi, [rows] ; all rows seen: no thumb to drag
cmp edi, [list.count]
jae @f
push sb
call [scrollbar_v_mouse]
mov eax, [sb.position]
xchg eax, [list.first]
sub eax, [list.first] ; moved while held: again after 10
jz @f ; waits of the main loop if pressed
mov eax, esi ; now, else after 2
and eax, MB_LEFT
jz @f
mov al, 2
test esi, MB_LEFT_DOWN
jz @f
mov al, 10
@@:
mov [repeat_at], eax
ret
sb_set: ; the list state to it
mov eax, [area_w]
add eax, PAD - SB_W
mov [sb.x_pos], ax
mov eax, [area_h]
mov [sb.y_size], ax
mov [sb.all_redraw], 1
mov edi, sb.max_area
mov eax, [list.count]
stosd
mov eax, [rows]
stosd
mov eax, [list.first]
stosd
mov eax, [sc.work_dark]
stosd
mov eax, [mid]
stosd
mov eax, [sc.work_graph]
stosd
ret
; Keep sel and first in range and remember the selected row.
list_clamp:
pusha
mov ecx, [list.count]
mov edi, list.sel
call clamp
jecxz @f
mov edx, [list.view]
mov eax, [edx + eax*4]
mov eax, [eax]
mov [list.key], eax
@@:
popa
pusha ; first in range
mov ecx, [list.count]
sub ecx, [rows]
inc ecx
mov edi, list.first
call clamp
popa
ret
clamp: ; [edi] into [0, ecx - 1]
mov eax, [edi]
cmp eax, ecx
jl @f
lea eax, [ecx - 1]
@@:
test eax, eax
jns @f
xor eax, eax
@@:
stosd
ret
list_key: ; al = key
mov esi, [list.keys]
.hotkey:
mov ah, [esi + HOTKEY.key]
test ah, ah
jz .move
add esi, sizeof.HOTKEY
cmp ah, al
jne .hotkey
movzx eax, [esi - sizeof.HOTKEY + HOTKEY.trigger]
jmp eax
.move:
sub al, KEY_DOWN
cmp al, KEY_PGUP - KEY_DOWN
ja .done
movzx eax, al
movsx edx, byte [.moves + eax*2 + 1]
imul edx, [rows]
movsx eax, byte [.moves + eax*2]
add edx, eax
add [list.sel], edx
call list_clamp
mov ebx, [list.sel] ; scroll to the selection
mov ecx, [rows]
sub ebx, ecx
inc ebx
sub [list.first], ebx
mov edi, list.first
call clamp
add [list.first], ebx
jmp list_rows
.done:
ret
; rows, pages for the keys KEY_DOWN..KEY_PGUP: down, up, right, home, end,
; del, pgdn, pgup
.moves db 1, 0, -1, 0, 0, 0, 0, -128, 0, 127, 0, 0, 0, 1, 0, -1
; Wheel scrolls; left button selects a row, sorts by a column or moves the
; scrollbar; right click opens the row.
list_mouse:
call get_mouse
imul eax, 3
call scroll_mouse ; every event: it follows a drag
test esi, MB_LEFT + MB_RIGHT_DOWN
jz .moved
sub ebx, PAD ; inside the list, left of the
jle .moved ; scrollbar
mov eax, [area_w]
sub eax, SB_W
cmp ebx, eax
jge .moved
sub ecx, AREA_Y + HDR_H + 2
jl .header
test esi, MB_LEFT_DOWN + MB_RIGHT_DOWN ; a row: not dragged to
jz .moved
xchg eax, ecx
xor edx, edx
mov ecx, ROW_H
div ecx
cmp eax, [rows]
jae .moved
add eax, [list.first]
cmp eax, [list.count]
jae .moved
mov [list.sel], eax
shr esi, 9 ; MB_RIGHT_DOWN: 0 left, 1 right
and esi, 1
push [list.left + esi*4] ; the selection drawn, then list_rows
jmp list_rows ; returns to the trigger
.moved: ; a drag of the thumb keeps its pixels
cmp ebp, [list.first]
jne list_rows
ret
.header:
cmp ecx, -(HDR_H + 1)
jl .moved
test esi, MB_LEFT_DOWN
jz .moved
mov esi, [list.cols] ; the column under the cursor
dec ebx
.col:
call col_width
sub ebx, eax
jl .sort
add esi, sizeof.COL
cmp esi, [list.cols_end]
jb .col
ret
.sort: ; again: the other way, else its own
xor eax, eax
cmp byte [esi + COL.kind], KIND_PCT
setae al
cmp esi, [list.sort]
jne .order
mov eax, [list.desc]
xor al, 1
.order:
mov [list.desc], eax
mov [list.sort], esi
call list_sort
jmp list_draw
; out: esi = mouse buttons held, in bits 8.. the ones pressed, not held at
; the last event, and the mouse not moved since; ebx, ecx = x, y in the
; client area; eax = the wheel
get_mouse:
mcall SF_MOUSE_GET, SSF_BUTTON_EXT
mov esi, eax
movzx eax, al
xchg al, byte [buttons]
shl eax, 8
not eax
and esi, eax
mcall SF_MOUSE_GET, SSF_WINDOW_POSITION
movsx ecx, ax ; in the client area
sar eax, 16
xchg edx, eax
mcall SF_MOUSE_GET, SSF_SCROLL_DATA
mov ebx, edx
movsx eax, ax ; the wheel
ret
no_keys db 0 ; HOTKEYs of a list without them
uglobal
align 4
list LIST
rows dd ? ; that fit the panel
col_rest dd ? ; the width of the widest column
repeat_at dd ? ; waits till a held arrow repeats
endg
+94
View File
@@ -0,0 +1,94 @@
; Strings in UTF-8; the caption starts with its encoding byte.
if lang eq ru_RU
sz_title db 3, "Диспетчер задач", 0
sz_tab_procs db "Процессы", 0
sz_tab_perf db "Нагрузка", 0
sz_tab_disks db "Диски", 0
sz_col_free db "Свободно", 0
sz_run db "Запустить...", 0
sz_kill db "Завершить", 0
sz_shutdown db "Завершение работы...", 0
sz_info db "Детали", 0
sz_system db "Системные", 0
sz_col_name db "Имя", 0
sz_col_cpu db "ЦП", 0
sz_col_mem db "Память", 0
sz_freq db "Частота", 0
sz_mhz db " МГц", 0
sz_uptime db "Время работы", 0
sz_threads db "Потоки", 0
sz_used db "Занято", 0
sz_total db "Всего", 0
sz_temp db "Температура", 0
sz_net db "Сеть", 0
sz_received db "Принято", 0
sz_sent db "Отправлено", 0
sz_crit db "Критическая", 0
sz_units db " КБ", 0, " МБ", 0, " ГБ", 0, " ТБ", 0
sz_bits db " бит/с", 0, " Кбит/с", 0, " Мбит/с", 0, " Гбит/с", 0
else if lang eq es_ES
sz_title db 3, "Gestor de tareas", 0
sz_tab_procs db "Procesos", 0
sz_tab_perf db "Recursos", 0
sz_tab_disks db "Discos", 0
sz_col_free db "Libre", 0
sz_run db "Ejecutar...", 0
sz_kill db "Finalizar", 0
sz_shutdown db "Apagar el sistema...", 0
sz_info db "Detalles", 0
sz_system db "Sistema", 0
sz_col_name db "Nombre", 0
sz_col_cpu db "CPU", 0
sz_col_mem db "Memoria", 0
sz_freq db "Frecuencia", 0
sz_uptime db "Activo", 0
sz_threads db "Hilos", 0
sz_used db "En uso", 0
sz_total db "Total", 0
sz_temp db "Temperatura", 0
sz_net db "Red", 0
sz_received db "Recibido", 0
sz_sent db "Enviado", 0
sz_crit db "Crítica", 0
else
sz_title db 3, "Task Manager", 0
sz_tab_procs db "Processes", 0
sz_tab_perf db "Performance", 0
sz_tab_disks db "Disks", 0
sz_col_free db "Free", 0
sz_run db "Run...", 0
sz_kill db "End", 0
sz_shutdown db "Shut down...", 0
sz_info db "Details", 0
sz_system db "System", 0
sz_col_name db "Name", 0
sz_col_cpu db "CPU", 0
sz_col_mem db "Memory", 0
sz_freq db "Frequency", 0
sz_uptime db "Uptime", 0
sz_threads db "Threads", 0
sz_used db "Used", 0
sz_total db "Total", 0
sz_temp db "Temperature", 0
sz_net db "Network", 0
sz_received db "Received", 0
sz_sent db "Sent", 0
sz_crit db "Critical", 0
end if
if ~ lang eq ru_RU
sz_mhz db " MHz", 0
sz_units db " KB", 0, " MB", 0, " GB", 0, " TB", 0
sz_bits db " bit/s", 0, " kbit/s", 0, " Mbit/s", 0, " Gbit/s", 0
end if
sz_col_id db "ID", 0
if lang eq ru_RU | lang eq es_ES
sz_col_num db "Nº", 0
else
sz_col_num db " #", 0
end if
sz_deg db " °C", 0
sz_pct db "%", 0
+386
View File
@@ -0,0 +1,386 @@
; Temperature on the Performance tab: the sensor of the CPU (Intel from
; Core, AMD from K8, Hygon, VIA C7, Nano and Zhaoxin; the rules are those
; of Linux coretemp, k8temp, k10temp and via-cputemp), else the first one
; of a monitoring chip of the board (hwmon.inc).
MSR_THERM_STATUS = 0x19C ; Intel: the core, the package,
MSR_PKG_THERM = 0x1B1 ; TjMax in bits 16-23
MSR_TEMP_TARGET = 0x1A2
MSR_VIA_TEMP = 0x1423 ; Nano and Zhaoxin, C7
MSR_C7_TEMP = 0x1169
AMD_NB = 0xC300 ; PCI 00:18.3, then its registers:
K8_THERMTRIP = 0xC3E4 ; K8, the others to Family 16h
NB_TEMP = 0xC3A4
VENDOR_AMD = 0x1022
VENDOR_HYGON = 0x1D94
SMN_ZEN_TEMP = 0x59800 ; the address, the index register
SMN_ZEN_INDEX = 0x60 ; on the root complex, 00:00.0
SMN_F15_TEMP = 0xD8200CA4 ; Family 15h from model 60h
SMN_F15_INDEX = 0xB8
; The sensor of the CPU, else the chip; the temperature graph if any.
sensors_init:
pusha
call cpu_sensor
cmp [cpu_temp], chip_temp
je .chip
mcall SF_THREAD_CONTROL, SSF_CREATE_THREAD, msr_probe, probe_stack_top
mov esi, 20 ; up to 0.2 s
@@:
cmp [probe], 2
je @f
mcall SF_SLEEP, 1
dec esi
jnz @b
@@:
cmp [probe], 0
jne @f
mov [cpu_temp], chip_temp ; the kernel killed the probe
@@:
cmp [tjmax], 0
jne .chip
mov byte [tjmax], 100
.chip:
cmp [cpu_temp], chip_temp ; the chip for a CPU without a sensor
jne .temp
call hwm_init
test eax, eax
jz @f
.temp:
inc [temp_on] ; a graph for any sensor
@@:
cmp [cpu_temp], intel_temp ; TjMax of Intel only
je @f
mov [temp_graph.info], no_info
@@:
popa
ret
; cpu_temp and what it reads, by the vendor and the model
cpu_sensor:
pushfd ; CPUID if the ID flag can be changed
pop eax
xor eax, 1 shl 21
push eax
popfd
pushfd
pop ecx
cmp eax, ecx
jne .done
xor eax, eax
cpuid
mov esi, eax ; the last leaf
mov edi, ebx ; vendor
mov eax, 1
cpuid
mov ebp, eax ; signature
movzx ecx, ah
and cl, 0xF ; family
movzx edx, al
shr edx, 4 ; model
cmp cl, 6
jb @f
shr eax, 12
and al, 0xF0
or dl, al
cmp cl, 0xF
jne @f
movzx eax, ah
add ecx, eax
@@:
cmp edi, "Genu"
je intel_sensor
cmp edi, " Sh" ; C7, Nano and Zhaoxin, the probe
je via_sensor ; drops the others
cmp edi, "Cent"
je via_sensor
cmp edi, "Auth"
je amd_sensor
cmp edi, "Hygo"
je amd_sensor
.done:
ret
; ecx = family, edx = model, esi = the last leaf of CPUID, ebp = signature
intel_sensor:
cmp esi, 6
jb .done
push ecx edx
mov eax, 6
cpuid
pop edx ecx
test al, 1 ; digital thermal sensor
jz .done
mov [cpu_temp], intel_temp
test al, 1 shl 6 ; the package one
jz @f
mov [therm_msr], MSR_PKG_THERM
mov [therm_valid], 1 shl 31 ; it has no "reading valid" bit
@@:
cmp ecx, 6 ; TjMax: 100 before Nehalem,
jne .done ; then from the CPU but on Atoms
cmp edx, 0x1A
jb .done
mov esi, .atoms
@@:
lodsw
test al, al
jz @f ; 0: msr_probe reads it
cmp al, dl
jne @b
@@:
mov byte [tjmax], ah
cmp dl, 0x1C ; Pineview: stepping 10
jne .done
and ebp, 0xF
cmp ebp, 10
jne .done
mov byte [tjmax], 100
.done:
ret
; TjMax of Atoms without MSR_TEMPERATURE_TARGET, and Xeon 7400
.atoms db 0x1C, 90, 0x1D, 100, 0x26, 90, 0x27, 90, 0x35, 90, 0x36, 100, 0, 0
; ecx = family, edx = model
via_sensor:
mov eax, MSR_VIA_TEMP
cmp ecx, 6
ja @f
cmp dl, 0xF
jae @f
mov ax, MSR_C7_TEMP
@@:
mov [therm_msr], eax
mov [cpu_temp], via_temp
ret
; ecx = family, edx = model
amd_sensor:
cmp ecx, 0xF
jb .done
ja .k10
push edx ; K8 with a thermal sensor
mov eax, 0x80000007
cpuid
test dl, 1
pop edx
jz .done
cmp dl, 0x69 ; revision G desktops read lower
jb @f
xchg eax, edx
mov edi, .k8_mobile
mov ecx, 3
repne scasb
je @f
mov [k8_zero], 49 - 21
@@:
mov esi, k8_temp
jmp .nb
.k10:
mov eax, SMN_ZEN_TEMP ; Zen
mov [smn_index], SMN_ZEN_INDEX
cmp ecx, 0x17
jae .smn
mov eax, SMN_F15_TEMP
mov [smn_index], SMN_F15_INDEX
cmp ecx, 0x15
jne @f
cmp edx, 0x60
jae .smn
@@:
cmp ecx, 0x16
ja .done
mov esi, amd_nb_temp
.nb:
mov ecx, AMD_NB
jmp .pci
.smn:
mov [smn_addr], eax
xor ecx, ecx
mov esi, amd_smn_temp
.pci: ; esi = the procedure, if the
call pci_read ; device is AMD or Hygon
cmp ax, VENDOR_AMD
je @f
cmp ax, VENDOR_HYGON
jne .done
@@:
mov [cpu_temp], esi
.done:
ret
.k8_mobile db 0x6C, 0x7C, 0xC1
; ecx = device shl 8 + register on bus 0; out: eax
pci_read: ; the callers keep ebx
mcall SF_PCI, SSF_READ_DWORD
ret
; out: eax = temperature of the CPU, CF = 1 if unknown
via_temp:
call read_therm
and eax, 0xFFFFFF
ret
; out: eax = the low dword of [therm_msr]
read_therm:
mcall SF_SYS_MISC, SSF_READ_MSR, , [therm_msr]
ret
intel_temp:
call read_therm
or eax, [therm_valid] ; the reading is valid
jns no_temp
shr eax, 16
and eax, 0x7F
neg eax
add eax, [tjmax]
clc
ret
k8_temp:
mov ecx, K8_THERMTRIP
call pci_read
shr eax, 16
movzx eax, al
sub eax, [k8_zero]
clc
ret
amd_smn_temp:
mcall SF_PCI, SSF_WRITE_DWORD, [smn_index], [smn_addr]
add ecx, 4 ; the data after the index
call pci_read
test eax, 1 shl 19 ; the range is -49..206
jz amd_temp
or eax, 3 shl 16
jmp amd_temp
amd_nb_temp:
mov ecx, NB_TEMP
call pci_read ; then amd_temp
; eax = the reported temperature of AMD
amd_temp:
mov edx, eax
shr eax, 24 ; 1/8 of a degree from bit 21
and edx, 3 shl 16
cmp edx, 3 shl 16
jne @f
sub eax, 49
@@:
clc
ret
chip_temp: ; none in the CPU: the first of the chip
mov eax, [hwm_temp]
jmp @b
no_temp:
stc
ret
; A thread reads the MSRs first: if one is not there, the kernel kills
; the thread, not the program.
msr_probe:
call [cpu_temp]
inc [probe]
cmp [tjmax], 0
jne @f
mcall SF_SYS_MISC, SSF_READ_MSR, , MSR_TEMP_TARGET
shr eax, 16
test al, al ; 0 in some virtual machines
jz @f
mov byte [tjmax], al
@@:
inc [probe]
mcall SF_TERMINATE_PROCESS
; Once a second: the sensors and the temperature for the graph
sensors_read:
pusha
xor eax, eax
call [hwm_read]
cmp eax, 127 ; 127 and more: no sensor
jb @f
xor eax, eax
@@:
mov [hwm_temp], eax
call [cpu_temp]
jnc .got
mov eax, [last_temp] ; not valid this time
.got:
mov [last_temp], eax
mov edi, last_temp ; not below 0
mov ecx, 1000
call clamp
mov ecx, [hist_pos]
mov [hist_temp + ecx*4], eax
popa
ret
; An ITEM of temp_info
get_tjmax:
mov eax, [tjmax]
call fmt_uint ; then deg_unit
deg_unit:
mov eax, sz_deg
jmp fmt_unit
cpu_temp dd chip_temp ; the procedure
therm_msr dd MSR_THERM_STATUS
therm_valid dd 0
tjmax dd 100
k8_zero dd 49
uglobal
align 4
temp_on dd ? ; a graph of the temperature
smn_addr dd ?
smn_index dd ?
last_temp dd ?
probe dd ? ; 1: the sensor is read, 2: done
rb 256
probe_stack_top:
hist_temp rd HIST_LEN
endg
+111
View File
@@ -0,0 +1,111 @@
; Statistics shared by the tabs, once a second: the threads, their CPU and
; memory, the history of the graphs.
struct PROC ; an item of `procs`
pid dd ?
cpu dd ? ; cycles / 256, then percent
mem dd ? ; KB
name rb 12 ; zero padded
ends
struct RAM_INFO ; function 18.20
total dd ? ; pages
free dd ?
page_faults dd ?
heap_size dd ? ; the kernel heap, bytes
heap_free dd ?
heap_blocks dd ?
heap_free_blocks dd ?
heap_max_free dd ?
heap_max_used dd ?
ends
; The threads, their CPU and memory, the history of the graphs.
sample:
pusha
mov edi, procs
xor edx, edx ; cycles of all threads / 256
xor ecx, ecx
.slot:
inc ecx
mcall SF_THREAD_INFO, pinfo
mov ebp, eax ; the last slot
cmp [pinfo.slot_state], TSTATE_FREE
je .next
mov eax, [ebx + process_information.PID]
stosd ; PROC.pid
mov eax, [ebx + process_information.cpu_usage]
shr eax, 8 ; the sum fits 32 bits
stosd ; PROC.cpu
add edx, eax
mov eax, [ebx + process_information.used_memory]
inc eax
shr eax, 10
stosd ; PROC.mem
lea esi, [ebx + process_information.process_name]
movsd ; PROC.name: 11 bytes and 0
movsd
movsd
mov byte [edi - 1], 0
cmp edi, procs + MAX_PROCS * sizeof.PROC
jae .done
.next:
cmp ecx, ebp
jb .slot
.done:
mov [procs_end], edi
; Percent of CPU = cycles of the thread / 1% of cycles of all threads.
xchg eax, edx
cdq
mov ecx, 100
div ecx
inc eax
xchg eax, ecx
mov esi, procs
@@:
mov eax, [esi + PROC.cpu]
cdq
div ecx
mov [esi + PROC.cpu], eax
add esi, sizeof.PROC
cmp esi, edi
jb @b
inc [hist_pos] ; the place of this sample
and [hist_pos], HIST_LEN - 1
mcall SF_SYSTEM, SSF_GET_RAM_INFO, ram_info
mov eax, [ram_info.total]
sub eax, [ram_info.free]
imul eax, 100
cdq
div [ram_info.total]
call sensors_read
call net_sample
mov ecx, [hist_pos]
mov [hist_mem + ecx*4], eax
mov eax, 100
sub eax, dword [procs + PROC.cpu] ; the first thread is IDLE
mov [hist_cpu + ecx*4], eax
popa
ret
uglobal
align 4
ram_info RAM_INFO
hist_pos dd ?
hist_cpu rd HIST_LEN
hist_mem rd HIST_LEN
procs_end dd ?
procs rb MAX_PROCS * sizeof.PROC
endg
+231
View File
@@ -0,0 +1,231 @@
; Autorun tab: the programs /sys/LAUNCHER starts from /sys/settings/autorun.dat.
; A line is: program parameters delay; "text" may hold spaces, # starts
; a comment. A program after # is turned off: the button or Space turns it
; on or off in the file.
struct AR
prog dd ?
params dd ?
delay dd ?
num dd ? ; of the entry
off db ? ; 1: after #, turned off
ends
; Read the file, its lines into the list.
autorun_load:
mcall SF_FILE, autorun_info ; its size; read a byte more to see
mov ecx, [autorun_bdfe.FileSizeLow] ; its end
test eax, eax
jz @f
xor ecx, ecx
@@:
inc ecx
mov [autorun_file.Count], ecx
inc ecx ; a # more, then 0
inc ecx
mcall SF_SYS_MISC, SSF_MEM_REALLOC, , [autorun_file.Buffer]
mov [autorun_file.Buffer], eax
mov [autorun_save.Buffer], eax
mov ebp, [list.view]
test eax, eax
jz list_done
mcall SF_FILE, autorun_file
cmp eax, 6 ; the end of the file: all of it
je @f
xor ebx, ebx
@@:
mov esi, [autorun_file.Buffer]
mov byte [esi + ebx], 0
add ebx, esi
mov edi, esi ; the rows: at most the bytes that
xor edx, edx ; may end a line, 0..13, and one
.count:
inc edx
@@:
cmp edi, ebx
jae @f
cmp byte [edi], 14
inc edi
jb .count
jmp @b
@@:
imul ecx, edx, 4 + sizeof.AR ; the view, then the rows
push ebx edx
mcall SF_SYS_MISC, SSF_MEM_REALLOC, , [list.view]
pop edx ebx
mov [list.view], eax
mov ebp, eax
lea edi, [eax + edx*4]
test eax, eax
jz list_done
.line:
cmp esi, ebx
jae list_done
push esi
@@:
lodsb ; find the end of the line
cmp al, 13
je @f
cmp al, 10
je @f
test al, al
jnz @b
@@:
mov byte [esi - 1], 0
xchg esi, [esp]
call get_token ; the row goes to edi, its pointer to ebp
cmp byte [eax], 0 ; empty line
je .next
cmp word [eax], "#/" ; a program turned off
je @f
cmp byte [eax], "#" ; comment
je .next
@@:
mov dl, [eax]
mov [ebp], edi
add ebp, 4
stosd ; AR.prog, params, delay
call get_token
stosd
call get_token
stosd
mov eax, ebp
sub eax, [list.view]
shr eax, 2
stosd
cmp dl, "#" ; AR.off
sete al
stosb
.next:
pop esi
jmp .line
; Put # before the selected program or take it away and save the file.
autorun_toggle:
cmp [list.count], 0 ; no rows, maybe no buffer
je .done
mcall SF_FILE, autorun_file ; the file again, as it is
cmp eax, 6 ; all of it
jne .done
mov edi, [list.key] ; AR.prog: the line in the buffer
mov ecx, [autorun_file.Buffer]
add ecx, ebx
sub ecx, edi ; not past the end: the file was cut
jbe .done
cmp byte [edi], "#"
je .on
lea esi, [edi + ecx - 1] ; one byte on from the end
lea edi, [esi + 1]
std
rep movsb
cld
mov byte [edi], "#"
inc ebx
jmp .save
.on:
lea esi, [edi + 1]
dec ecx
rep movsb
dec ebx
.save:
mov [autorun_save.Count], ebx
mcall SF_FILE, autorun_save
call autorun_load
jmp list_rows
.done:
ret
; esi -> text; out: eax -> the next word or "text", cut with zero; esi after it
get_token:
mov eax, esi
mov dh, [esi]
test dh, dh
jz .done
inc esi
cmp dh, " " ; skip blanks
jbe get_token
mov cx, " " shl 8 ; a word ends with a blank
cmp dh, '"'
jne .next
mov cx, '"' ; "text" with the quote
mov eax, esi
.next:
mov dh, [esi]
test dh, dh
jz .done
inc esi
sub dh, cl ; cl..cl + ch ends it
cmp dh, ch
ja .next
mov byte [esi - 1], 0
.done:
ret
autorun_bar:
BAR BAR_BUTTON, sz_ar_toggle, sc.work_button
db BAR_END
autorun_list LIST autorun_cols, autorun_cols_end, autorun_cols, nothing, nothing, autorun_keys, AR.off, 0, 0, 0, 0, 0, 0 ; the view on the heap
autorun_keys:
HOTKEY KEY_SPACE, autorun_toggle
db 0
autorun_cols:
COL sz_col_num, AR.num, KIND_NUM, ID_W
COL sz_col_program, AR.prog, KIND_STR, 0
COL sz_col_params, AR.params, KIND_STR, COL_W
COL sz_col_delay, AR.delay, KIND_STR, COL_W
autorun_cols_end:
if lang eq ru_RU
sz_tab_autorun db "Автозагрузка", 0
sz_col_program db "Программа", 0
sz_col_params db "Параметры", 0
sz_col_delay db "Задержка", 0
sz_ar_toggle db "Включить/выключить", 0
else if lang eq es_ES
sz_tab_autorun db "Inicio", 0
sz_col_program db "Programa", 0
sz_col_params db "Parámetros", 0
sz_col_delay db "Retardo", 0
sz_ar_toggle db "Activar/desactivar", 0
else
sz_tab_autorun db "Autorun", 0
sz_col_program db "Program", 0
sz_col_params db "Parameters", 0
sz_col_delay db "Delay", 0
sz_ar_toggle db "Turn on/off", 0
end if
autorun_info FileInfoBlock SSF_GET_INFO, 0, 0, 0, autorun_bdfe, 0, sz_autorun
autorun_file FileInfoBlock SSF_READ_FILE, 0, 0, 0, 0, 0, sz_autorun ; the buffer on
autorun_save FileInfoBlock SSF_CREATE_FILE, 0, 0, 0, 0, 0, sz_autorun ; the heap
sz_autorun db "/sys/settings/autorun.dat", 0
virtual at tab_data ; only the open tab uses it
autorun_bdfe FileInfoA
AUTORUN_DATA = $ - tab_data
end virtual
+343
View File
@@ -0,0 +1,343 @@
; Disks tab: the partitions in a sortable list, the used space as a bar
; across what the other columns leave. The kernel does not report free
; space, so for memory disks (/rd, /tmp*) the space taken by files is summed
; up. The disks are found in a thread, the tab is drawn at once.
struct DISK
num dd ? ; in the order found, the key of the row
icon dd ? ; in ICONS18, by the kind of the disk
fill dd ? ; used, 1/1024 of the size, -1: unknown
used dd ? ; KB, -1: unknown
total dd ? ; KB
name rb 16 ; the path
label rb 28
ends
MAX_DISKS = 16
WALK_MAX = 1024 ; path length
WALK_DEPTH = 16
; Start the thread that finds the disks.
disks_scan:
xor eax, eax
mov [list.count], eax
mov [list.first], eax
mov [disk_n], eax
mov [walk_stop], eax
inc [walking]
mcall SF_THREAD_CONTROL, SSF_CREATE_THREAD, walker, walk_stack_top
inc eax ; -1: no thread, nothing to wait for
jnz @f
and [walking], 0
@@:
ret
; The walker found more: the view of the disks found so far, sorted.
disks_update:
xor eax, eax
xchg eax, [walk_new]
test eax, eax
jz .done
mov esi, disks
mov ebp, disks_view
mov ecx, [disk_n]
@@:
mov [ebp], esi
add ebp, 4
add esi, sizeof.DISK
loop @b
call list_done
jmp list_rows
.done:
ret
disks_open: ; the selected disk in Eolite
cmp [list.count], 0
je .done
imul edx, [list.key], sizeof.DISK ; DISK.num
add edx, disks + DISK.name
mov esi, sz_eolite_path
jmp run_app
.done:
ret
;-------------------------------------------------------------------------------
; The thread that finds the disks and sums up the files of memory disks.
disks_find:
xor ebp, ebp ; device index in "/"
.device:
mov word [walk_path], "/"
call read_entry
jnz .done
mov edi, walk_path + 1
call fmt_str
mov ecx, edi ; end of "/device"
xor edx, edx ; partition index
.partition:
mov byte [ecx], 0
xchg ebp, edx
call read_entry
xchg ebp, edx
jnz .device
cmp [disk_n], MAX_DISKS
jae .done
mov edi, ecx
mov byte [edi], "/"
inc edi
call fmt_str
call disk_add
jmp .partition
.done:
ret
; Read entry ebp of the folder [walk_path], ebp to the next one; out:
; ZF = 1 if read, esi -> its name
read_entry:
cmp [walk_stop], 0 ; asked to stop: the thread ends
jne walker.stop
mov [fs_read.Position], ebp
inc ebp
mcall SF_FILE, fs_read
mov esi, fs_entry.FileName
test eax, eax
ret
; [walk_path] = root of the partition, edi -> its end
disk_add:
pusha
mcall SF_FILE, fs_info
test eax, eax
jnz .done
mov eax, [disk_n]
imul ebx, eax, sizeof.DISK
add ebx, disks
mov [ebx + DISK.num], eax
mov ax, word [walk_path + 1] ; the kind by the device
mov esi, disk_icons - 3
@@:
add esi, 3
cmp word [esi], 0
je @f
cmp [esi], ax
jne @b
@@:
movzx eax, byte [esi + 2]
mov [ebx + DISK.icon], eax
mov esi, walk_path
lea edi, [ebx + DISK.name]
mov ecx, DISK.label - DISK.name
call copy_cut
mov esi, fs_buf + FileInfoA.FileName ; of the partition
lea edi, [ebx + DISK.label]
mov ecx, sizeof.DISK - DISK.label
call copy_cut
mov eax, [fs_buf + FileInfoA.FileSizeLow]
mov edx, [fs_buf + FileInfoA.FileSizeHigh]
shrd eax, edx, 10
shr edx, 10 ; 4 TB and more: unknown
jz @f
or eax, -1
@@:
mov [ebx + DISK.total], eax
or dword [ebx + DISK.used], -1
or dword [ebx + DISK.fill], -1
inc [disk_n] ; listed when it is all there
mov [walk_new], ebx
test eax, eax
jz .done
mov eax, dword [walk_path + 1]
cmp ax, "rd"
je @f
and eax, 0xFFFFFF
cmp eax, "tmp"
jne .done
@@:
xor ecx, ecx
xor edx, edx
mov edi, [esp + PUSHA_EDI] ; the end of the path
call disk_walk
cmp ecx, [ebx + DISK.total] ; not more used than there is
jbe @f
mov ecx, [ebx + DISK.total]
@@:
mov [ebx + DISK.used], ecx
xchg eax, ecx
mov edx, 1024
mul edx
div dword [ebx + DISK.total]
mov [ebx + DISK.fill], eax
mov [walk_new], ebx
.done:
popa
ret
; esi -> text, edi -> a field of ecx bytes: the text, cut to fit it, not in
; the middle of a character
copy_cut:
rep movsb
@@:
dec edi
mov al, [edi]
and al, 0xC0
cmp al, 0x80
je @b
mov [edi], cl
ret
; The thread.
walker:
call disks_find
.stop:
and [walking], 0
mcall SF_TERMINATE_PROCESS
; Ask the walker to stop and wait till it is gone.
stop_walker:
mov [walk_stop], 1
@@:
and [walk_new], 0
cmp [walking], 0
je @f
mcall SF_SLEEP, 1
jmp @b
@@:
ret
; Add the size of files under [walk_path] to ecx, KB; edi -> end of the path,
; edx = depth
disk_walk:
pusha
xor ebp, ebp ; index in the folder
inc edx
.next:
call read_entry
jnz .done
test byte [fs_entry.Attributes], FA_LABEL
jnz .next
test byte [fs_entry.Attributes], FA_FOLDER
jnz .folder
mov eax, [fs_entry.FileSizeLow]
add eax, 1023
shr eax, 10
add ecx, eax
jmp .next
.folder:
cmp byte [esi], "." ; skip "." and ".."
jne @f
cmp byte [esi + 1], 0
je .next
cmp word [esi + 1], "."
je .next
@@:
cmp edx, WALK_DEPTH
jae .next
lea eax, [edi + 264]
cmp eax, walk_path + WALK_MAX
jae .next
push edi
mov byte [edi], "/"
inc edi
call fmt_str
call disk_walk
pop edi
mov byte [edi], 0
jmp .next
.done:
mov [esp + PUSHA_ECX], ecx
popa
ret
disks_bar:
BAR BAR_BUTTON, sz_open, sc.work_button
db BAR_END
disks_list LIST disks_cols, disks_cols_end, disks_cols + sizeof.COL, nothing, disks_open, disks_keys, 0, 0, 0, 0, 0, 0, tab_data
disks_keys:
HOTKEY KEY_ENTER, disks_open
db 0
disks_cols:
COL sz_col_num, DISK.icon, KIND_ICON, ID_W
COL sz_col_name, DISK.name, KIND_TEXT, COL_W
COL sz_col_label, DISK.label, KIND_TEXT, COL_W
COL sz_pct, DISK.fill, KIND_BAR, 0
COL sz_used, DISK.used, KIND_SIZE, COL_W
COL sz_col_size, DISK.total, KIND_SIZE, COL_W
disks_cols_end:
if lang eq ru_RU
sz_col_size db "Размер", 0
sz_col_label db "Метка", 0
sz_open db "Открыть", 0
else if lang eq es_ES
sz_col_size db "Tamaño", 0
sz_col_label db "Etiqueta", 0
sz_open db "Abrir", 0
else
sz_col_size db "Size", 0
sz_col_label db "Label", 0
sz_open db "Open", 0
end if
sz_eolite_path db "/sys/File Managers/EOLITE", 0
; the first two letters of the device, the icon; the last one for the others
disk_icons db "rd", 58, "tm", 51, "fd", 5, "cd", 19, "us", 57, 0, 0, 50
fs_read FileInfoBlock SSF_READ_FOLDER, 0, 1, 1, fs_buf, 0, walk_path ; cp866
; names, one entry from Position on
fs_info FileInfoBlock SSF_GET_INFO, 0, 3, 0, fs_buf, 0, walk_path ; UTF-8
; volume label
uglobal
disk_n dd ? ; the disks the walker found
walking dd ?
walk_stop dd ?
walk_new dd ?
rb 1024
walk_stack_top:
endg
virtual at tab_data ; only the open tab uses it
disks_view rd MAX_DISKS
disks rb MAX_DISKS * sizeof.DISK
fs_buf FileInfoHeader ; then an entry of a folder; a
fs_entry FileInfoA ; partition from fs_buf on
walk_path rb WALK_MAX
DISKS_DATA = $ - tab_data
end virtual
+92
View File
@@ -0,0 +1,92 @@
; Drivers tab: the drivers loaded by the kernel (function 68.31).
struct DRV
num dd ? ; in the list, the key of the row
name rb 16 ; from here on the item of the kernel
next dd ?
handle dd ? ; the kernel keeps the previous item here
base dd ?
start dd ? ; the entry of the driver
service dd ?
ends
MAX_DRIVERS = 64
; The drivers the kernel has now.
drivers_scan:
mcall SF_SYS_MISC, SSF_GET_DRIVER_DATA, 1
mov esi, ecx ; the last driver
mov edx, ebx ; the first one
mov ebp, drivers_view
mov edi, drivers + DRV.name
.next:
cmp ebp, drivers_view + MAX_DRIVERS*4
jae list_done
mcall SF_SYS_MISC, SSF_GET_DRIVER_DATA, 2
test eax, eax
jnz list_done
lea eax, [edi - DRV.name]
mov [ebp], eax
add ebp, 4
lea eax, [ebp - drivers_view]
shr eax, 2
mov dword [edi + DRV.num - DRV.name], eax
mov eax, dword [edi + DRV.next - DRV.name]
mov dword [edi + DRV.handle - DRV.name], edx
add edi, sizeof.DRV
cmp edx, esi
mov edx, eax
jne .next
jmp list_done
drivers_load: ; RUN with the name of the driver to type
mov esi, sz_run_path
mov edx, sz_loaddrv
jmp run_app
drivers_bar:
BAR BAR_BUTTON, sz_load, sc.work_button
db BAR_END
drivers_list LIST drivers_cols, drivers_cols_end, drivers_cols, nothing, nothing, no_keys, 0, 0, 0, 0, 0, 0, tab_data
drivers_cols:
COL sz_col_num, DRV.num, KIND_NUM, ID_W
COL sz_col_name, DRV.name, KIND_TEXT, 0
COL sz_col_handle, DRV.handle, KIND_HEX, COL_W
COL sz_col_base, DRV.base, KIND_HEX, COL_W
COL sz_col_service, DRV.service, KIND_HEX, COL_W
drivers_cols_end:
if lang eq ru_RU
sz_tab_drivers db "Драйверы", 0
sz_col_handle db "Дескриптор", 0
sz_col_base db "База", 0
sz_col_service db "Сервис", 0
sz_load db "Загрузить...", 0
else if lang eq es_ES
sz_tab_drivers db "Controladores", 0
sz_col_handle db "Manejador", 0
sz_col_base db "Base", 0
sz_col_service db "Servicio", 0
sz_load db "Cargar...", 0
else
sz_tab_drivers db "Drivers", 0
sz_col_handle db "Handle", 0
sz_col_base db "Base", 0
sz_col_service db "Service", 0
sz_load db "Load...", 0
end if
sz_loaddrv db "/sys/LOADDRV ", 0
virtual at tab_data ; only the open tab uses it
drivers_view rd MAX_DRIVERS
drivers rb MAX_DRIVERS * sizeof.DRV
DRIVERS_DATA = $ - tab_data
end virtual
+532
View File
@@ -0,0 +1,532 @@
; Performance tab: CPU, temperature when there is a sensor, memory and
; network history in rows, lines between them. A row: the icon, the title
; and the items on the left, the graph on the right.
GRAPH_STEP = 4 ; pixels per sample
INFO_W = 256 + 2*PAD + ICON ; the icon, the title, the items, PAD
NET_DEVICES = 16 ; of the kernel
struct G ; a graph
title dd ?
hist dd ? ; dwords up to the top
hist2 dd ? ; 0, the network: received, sent
info dd ? ; items left of the graph
unit dd ? ; of the value in the title
top dd ? ; -> the top of the scale
on dd ? ; -> not 0 if shown
icon dd ? ; before the title
ends
struct ITEM ; a line left of a graph
text dd ?
get dd ? ; edi -> the text; out: edi after it
ends
struct NET_BYTES ; of all the devices
rx dq ?
tx dq ?
ends
perf_draw: ; eax = TAB.draw: with the frames,
lea esi, [eax - TAB.update] ; TAB.update: esi = 0, without
mov edi, shown ; the graphs there is data for
mov eax, graphs
.shown:
mov edx, [eax + G.on]
cmp dword [edx], 0
je @f
stosd
@@:
add eax, sizeof.G
cmp eax, graphs_end
jb .shown
sub edi, shown
shr edi, 2
xchg edi, [shown_n]
cmp edi, [shown_n] ; one came or went: all again
je .draw
test esi, esi
jz draw_window
.draw:
xor ebp, ebp
.graph:
call graph_frame
call perf_graph
inc ebp
cmp ebp, [shown_n]
jb .graph
ret
; The button of the bar.
perf_cpuid:
mov esi, sz_cpuid_path
jmp run_plain
; ebp = graph; out: ebx = x shl 16 + w, ecx = y shl 16 + h of its frame.
; Between the rows a line, PAD from both.
graph_frame:
mov eax, [area_h]
dec eax
xor edx, edx
div [shown_n] ; from a row to the next one
mov ecx, ebp
imul ecx, eax
add ecx, AREA_Y + 1 + PAD
shl ecx, 16
sub eax, 2*PAD + 1
mov cx, ax
mov ebx, (3*PAD + 1 + INFO_W) shl 16
mov bx, word [area_w]
sub bx, 2 + 3*PAD + INFO_W
ret
; ecx = y shl 16 of a frame: the line between its row and the one above
row_line:
pusha
shr ecx, 16
sub ecx, PAD + 1 ; the middle of the gap above
shl ecx, 16
inc ecx
mov ebx, (PAD + 1) shl 16
mov bx, word [area_w]
sub bx, 2
mcall SF_DRAW_RECT, , , [sc.work_graph]
popa
ret
; ebp = graph, ebx and ecx = its frame, esi = 0: no frame
perf_graph:
pusha
test esi, esi
jz .title
mov edx, [sc.work_light]
call frame_rect
call row_line ; the first one on the panel border
.title:
mov ebp, [shown + ebp*4]
mov esi, [ebp + G.hist]
mov edx, [ebp + G.hist2]
mov edi, [hist_pos] ; the last value
mov eax, [esi + edi*4]
test edx, edx
jz @f
add eax, [edx + edi*4] ; the network: both ways
mov edi, text_buf
call fmt_rate
jmp .titled
@@:
mov edi, text_buf
call fmt_uint
mov eax, [ebp + G.unit]
call fmt_unit
.titled:
call info_lines ; the lines left of it
mov eax, [ebp + G.top] ; the top: 100 %, TjMax, the network:
mov eax, [eax] ; the largest rate of the columns
add ebx, 1 shl 16 - 2 ; bars inside the frame, newest on the right
add ecx, 1 shl 16 - 2
movzx edi, bx
test edx, edx
jz .scaled
call net_scale
.scaled:
mov [scale], eax
mov ebp, [hist_pos]
.bar: ; the network: received on the left,
mov eax, GRAPH_STEP ; sent on the right, lighter
cmp edi, eax
jae @f
mov eax, edi
@@:
mov bx, ax
sub edi, eax
pusha
shl edi, 16
add ebx, edi
mov edi, [sc.work_button]
test edx, edx
jz .last
mov eax, ebx
shr ax, 1
sub bx, ax
call .value
shl ebx, 16
add eax, ebx
xchg eax, ebx
mov esi, edx
mov eax, [sc.work_light] ; sent: half way to the background
mov edx, edi
call mix
xchg eax, edi
.last:
call .value
popa
dec ebp
and ebp, HIST_LEN - 1
test edi, edi
jnz .bar
test edx, edx ; the network: its top at the top right
jz .critical
mov edi, text_buf
mov eax, [scale]
call fmt_rate
mov ebx, [area_w]
shl ebx, 16
mov bx, [esp + PUSHA_ECX + 2] ; y of the frame
add ebx, ((-6 - INFO_W + ICON + PAD) shl 16) + 4
call info_value
@@:
popa
ret
.critical: ; the temperature: a line 10 below
cmp esi, hist_temp ; the top
jne @b
movzx eax, cx
imul eax, 10
div [scale] ; edx = G.hist2 = 0 here
shl eax, 16
add ecx, eax
mov cx, 1
mov ebx, [esp + PUSHA_EBX] ; the frame
add ebx, 1 shl 16 - 2
mcall SF_DRAW_RECT, , , [red]
jmp @b
; esi + ebp*4 -> value, edi = color: its bar at the bottom, the background
; above it, not one over the other, so it does not flicker
.value:
pusha
mov eax, [esi + ebp*4]
cmp eax, [scale] ; not over the top
jb @f
mov eax, [scale]
@@:
movzx esi, cx
mul esi
div [scale]
xchg ebp, eax
sub cx, bp
mcall SF_DRAW_RECT, , , [sc.work_light]
movzx edx, cx
shl edx, 16
add ecx, edx
mov cx, bp
mcall SF_DRAW_RECT, , , edi
popa
ret
; esi, edx -> the history received and sent, edi = the width of the graph;
; out: eax = its top, the largest rate shown up to 1, 2, 5, 10, 20, 50...
; bit/s while it fits
net_scale:
push edi
mov ebp, [hist_pos]
xor eax, eax
.max:
cmp eax, [esi + ebp*4]
jae @f
mov eax, [esi + ebp*4]
@@:
cmp eax, [edx + ebp*4]
jae @f
mov eax, [edx + ebp*4]
@@:
dec ebp
and ebp, HIST_LEN - 1
sub edi, GRAPH_STEP
jg .max
pop edi
cmp eax, 2000000000
ja .done
xor ebp, ebp
inc ebp
@@:
cmp ebp, eax
jae @f
add ebp, ebp
cmp ebp, eax
jae @f
lea ebp, [ebp*4 + ebp]
shr ebp, 1
cmp ebp, eax
jae @f
add ebp, ebp
jmp @b
@@:
xchg eax, ebp
.done:
ret
; text_buf on the right of an item line at ebx = x shl 16 + y
info_value:
pusha
mov ecx, [sc.work_text]
mov edx, text_buf
mov eax, edx
call text_width
neg eax
add eax, INFO_W - ICON - PAD
shl eax, 16
add ebx, eax
call draw_text
popa
ret
; ebp -> graph, ecx = y shl 16 + h of its frame, text_buf = its value:
; the title and the items a line each as fit, the label on the left, the
; value on the right. The ITEMs end with 0.
info_lines:
pusha
movzx esi, cx ; the height left
mov ebx, ((2*PAD + 1) shl 16) + INFO_W
mcall SF_DRAW_RECT, , , [sc.work_light]
mov ebx, ecx ; the icon, the lines after it
shr ebx, 16
add ebx, (2*PAD + 1) shl 16 - 1
mov eax, [ebp + G.icon]
mov edx, [sc.work_light]
call draw_icon
add ebx, ((ICON + PAD) shl 16) + 1
mov edx, [ebp + G.title]
mov ebp, [ebp + G.info]
mov ecx, [sc.work_text]
jmp .line
.item:
mov edx, [ebp + ITEM.text]
test edx, edx
jz .done
mov edi, text_buf
pusha
call [ebp + ITEM.get]
popa
add ebp, sizeof.ITEM
.line:
sub esi, 16
jb .done
call draw_text
mov eax, edx ; the value in the rest of the line
call text_width
pusha
mov ebx, INFO_W - 8 - ICON - PAD
mov edx, text_buf
mov edi, edx
call fmt_clip
popa
call info_value
add ebx, 16
jmp .item
.done:
popa
ret
; Once a second: the bytes of the network devices, but the loopback.
net_sample:
pusha
mov edi, net_sum
xor eax, eax
mov ecx, (net_last - net_sum) / 4 ; and net_up
rep stosd
mov ebx, (NET_DEVICES - 1) shl 8
.device:
push ebx ; bl = SSF_DEVICE_TYPE
mcall SF_NETWORK_GET
test eax, eax ; -1: none, 0: the loopback
jle .next
inc [net_up]
mov bl, SSF_RX_BYTE_COUNT
mcall SF_NETWORK_GET
add dword [net_sum.rx], eax
adc dword [net_sum.rx + 4], ebx
mov ebx, [esp]
mov bl, SSF_TX_BYTE_COUNT
mcall SF_NETWORK_GET
add dword [net_sum.tx], eax
adc dword [net_sum.tx + 4], ebx
.next:
pop ebx
dec bh
jns .device
mov ecx, [hist_pos]
mov esi, net_sum.rx
mov ebx, hist_rx
call .rate
mov ebx, hist_tx
call .rate
or [net_primed], -1
popa
ret
.rate: ; since the last second, 0 at first
mov eax, [esi]
mov edx, [esi + 4]
sub eax, [edi]
sbb edx, [edi + 4]
movsd ; the sum is the last one now
movsd
jae @f
xor eax, eax ; a device went away
@@:
shl eax, 3 ; bits
and eax, [net_primed]
mov [ebx + ecx*4], eax
ret
; The items of the lines left of the graphs: edi -> the text; out:
; edi after it.
get_freq:
mcall SF_SYSTEM, SSF_GET_CPU_FREQUENCY
mov ecx, 1000000
call fmt_quot
mov eax, sz_mhz
jmp fmt_unit
get_uptime:
mcall SF_SYSTEM_GET, SSF_TIME_COUNT
xor edx, edx
mov ecx, 100
div ecx
jmp fmt_time
get_threads:
mov eax, [procs_end]
sub eax, procs
mov ecx, sizeof.PROC ; then fmt_quot
; eax / ecx, edx = the remainder
fmt_quot:
xor edx, edx
div ecx
jmp fmt_uint
get_used:
mov eax, [ram_info.total]
sub eax, [ram_info.free]
jmp @f
get_free:
mov eax, [ram_info.free]
jmp @f
get_total:
mov eax, [ram_info.total]
@@:
shl eax, 2 ; pages to KB
jmp fmt_size
get_rx:
mov esi, net_sum.rx
mov ebx, hist_rx
jmp @f
get_tx:
mov esi, net_sum.tx
mov ebx, hist_tx
@@: ; the rate, then the total
mov eax, [hist_pos]
mov eax, [ebx + eax*4]
call fmt_rate
mov word [edi], " ("
scasw
mov eax, [esi]
mov edx, [esi + 4]
shrd eax, edx, 10
call fmt_size
mov word [edi], ")"
inc edi
ret
graphs:
G sz_col_cpu, hist_cpu, 0, cpu_info, sz_pct, pct_top, pct_top, 48
temp_graph G sz_temp, hist_temp, 0, temp_info, sz_deg, tjmax, temp_on, 68
G sz_col_mem, hist_mem, 0, mem_info, sz_pct, pct_top, pct_top, 51
G sz_net, hist_rx, hist_tx, net_info, 0, pct_top, net_up, 12
graphs_end:
pct_top dd 100
perf_bar:
BAR BAR_BUTTON, sz_cpuid_path + 5, sc.work_button ; "CPUID"
db BAR_END
sz_cpuid_path db "/sys/CPUID", 0
cpu_info ITEM sz_freq, get_freq
ITEM sz_uptime, get_uptime
ITEM sz_threads, get_threads
dd 0
mem_info ITEM sz_used, get_used
ITEM sz_col_free, get_free
ITEM sz_total, get_total
dd 0
net_info ITEM sz_received, get_rx
ITEM sz_sent, get_tx
dd 0
temp_info ITEM sz_crit, get_tjmax ; Intel
no_info dd 0
uglobal
align 4
shown rd 4 ; -> graphs
shown_n dd ?
net_primed dd ?
scale dd ? ; of the bars being drawn
net_sum NET_BYTES
net_up dd ? ; the devices, but the loopback
net_last NET_BYTES
hist_rx rd HIST_LEN ; bits a second
hist_tx rd HIST_LEN
endg
+98
View File
@@ -0,0 +1,98 @@
; Processes tab: the threads in a sortable list.
; The threads to show, then the whole list or the rows only.
procs_draw:
call procs_filter
jmp list_draw
procs_update:
call procs_filter
jmp list_rows
; The threads to show, without the system ones unless asked.
procs_filter:
mov esi, procs
mov ebp, procs_view
.next:
cmp esi, [procs_end]
jae list_done
cmp [show_system], 0
jne .add
cmp esi, procs ; IDLE
je .skip
cmp byte [esi + PROC.name], "@"
je .skip
cmp dword [esi + PROC.name], "OS"
je .skip
.add:
mov [ebp], esi
add ebp, 4
.skip:
add esi, sizeof.PROC
jmp .next
procs_button: ; edx = button id
jmp [.btns + (edx - BTN_OWN)*4]
.btns dd .system, procs_info, procs_kill, procs_run ; as in procs_bar
.system:
xor [show_system], 1
jmp draw_window
; The buttons: Run..., End, Details.
procs_run:
mov esi, sz_run_path
jmp run_plain
procs_kill: ; the key of a row is its PID
mcall SF_SYSTEM, SSF_TERMINATE_THREAD_ID, [list.key]
ret
procs_info:
mcall SF_SYSTEM, SSF_GET_THREAD_SLOT, [list.key]
test eax, eax
jz .done
mov edi, text_buf
call fmt_uint
mov esi, sz_tinfo_path
mov edx, text_buf
jmp run_app
.done:
ret
procs_bar:
BAR BAR_CHECK, sz_system, show_system
BAR BAR_BUTTON, sz_info, sc.work_button
BAR BAR_BUTTON, sz_kill, red
BAR BAR_GAP, 0, 0 ; away from End
BAR BAR_BUTTON, sz_run, green
db BAR_END
procs_list LIST procs_cols, procs_cols_end, procs_cols, nothing, procs_info, procs_keys, 0, 0, 0, 0, 0, 0, tab_data
procs_keys:
HOTKEY KEY_ENTER, procs_info
HOTKEY KEY_DEL, procs_kill
db 0
procs_cols:
COL sz_col_id, PROC.pid, KIND_NUM, ID_W
COL sz_col_name, PROC.name, KIND_TEXT, 0
COL sz_col_cpu, PROC.cpu, KIND_PCT, COL_W
COL sz_col_mem, PROC.mem, KIND_SIZE, COL_W
procs_cols_end:
uglobal
show_system db ?
endg
virtual at tab_data ; only the open tab uses it
procs_view rd MAX_PROCS
PROCS_DATA = $ - tab_data
end virtual
+344
View File
@@ -0,0 +1,344 @@
; SPDX-License-Identifier: GPL-2.0-only
;
; Task manager. A tab is a row of the `tabs` table: its caption, list
; and handlers. To add a tab, add a row and an .inc file with the handlers.
include "../../KOSfuncs.inc"
include "../../macros.inc"
include "../../develop/libraries/box_lib/box_lib.mac"
include "../../develop/libraries/libs-dev/libio/libio.inc"
format meos 01
entry start
include "gui.inc"
include "hwmon.inc"
include "list.inc"
include "locale.inc"
include "sensors.inc"
include "stats.inc"
include "window.inc"
include "tabs/autorun.inc"
include "tabs/disks.inc"
include "tabs/drivers.inc"
include "tabs/perf.inc"
include "tabs/procs.inc"
;-------------------------------------------------------------------------------
start:
mcall SF_SET_EVENTS_MASK, EVENTS
mcall SF_SYS_MISC, SSF_LOAD_DLL, box_lib_path
test eax, eax
jz exit
xchg ecx, eax
mov ebp, box_lib ; the names become the procs
.import:
mov ebx, ecx
.export:
mov esi, [ebx]
test esi, esi
jz exit
add ebx, 8
mov edi, [ebp]
@@:
lodsb
scasb
jne .export
test al, al
jnz @b
mov edx, [ebx - 4]
mov [ebp], edx
add ebp, 4
cmp dword [ebp], 0
jne .import
mcall SF_SYS_MISC, SSF_HEAP_INIT
mcall SF_SYS_MISC, SSF_MEM_OPEN, icons_name, 0, 0 ; of @RESHARE
mov [icons], eax
call sensors_init
reopen:
call open_tab
redraw:
call draw_window
still:
mcall SF_SYSTEM_GET, SSF_TIME_COUNT
mov ebx, [next_tick]
sub ebx, eax
jg .wait
add eax, TICK
mov [next_tick], eax
call sample
mcall SF_THREAD_INFO, pinfo, -1
test [pinfo.wnd_state], WND_MINIMIZED + WND_ROLLED_UP
jnz still
mov eax, TAB.update
call tab_call
jmp still
.wait:
cmp [repeat_at], 0 ; an arrow held: the mouse again soon
je @f
mov ebx, 4
@@:
mcall SF_WAIT_EVENT_TIMEOUT
dec eax ; EV_REDRAW
jz redraw
dec eax ; EV_KEY
jz key
dec eax ; EV_BUTTON
jz button
cmp al, EV_MOUSE - EV_BUTTON ; the mouse, or the time with
je .mouse ; it held
cmp al, EV_IDLE - EV_BUTTON
jne still
cmp [repeat_at], 0 ; a held arrow when it is time
je still
dec [repeat_at]
jnz still
.mouse:
mov eax, TAB.mouse
call tab_call
jmp still
key:
mcall SF_GET_KEY
shr eax, 8
cmp al, KEY_ESC
je exit
cmp al, KEY_TAB ; the next tab
je next_tab
call tab_list ; other keys are for lists
jz still
call list_key
jmp still
button:
mcall SF_GET_BUTTON
shr eax, 8
cmp eax, 1
je exit
cmp eax, BTN_OWN
jae .own
sub eax, BTN_TAB
cmp eax, BTN_SHUTDOWN - BTN_TAB
jb switch_tab
mov esi, sz_end_path
call run_plain
jmp still
.own:
xchg eax, edx
mov eax, TAB.button
call tab_call
jmp still
next_tab:
mov eax, [cur_tab]
inc eax
cmp eax, NTABS
jb switch_tab
xor eax, eax ; then switch_tab
switch_tab: ; eax = tab
cmp eax, [cur_tab] ; the open one: nothing to do
je still
push eax
call tab_list ; the old tab keeps its list
mov esi, list
call copy_list
pop [cur_tab]
jmp reopen
exit:
call stop_walker
mcall SF_TERMINATE_PROCESS
; Open the current tab: its list, then its activate handler.
open_tab:
and [repeat_at], 0
call stop_walker
call tab_list
mov esi, edi
mov edi, list
call copy_list
mov eax, TAB.activate
tab_call: ; eax = handler in TAB
imul ecx, [cur_tab], sizeof.TAB
movzx ecx, word [tabs + ecx + eax]
jmp ecx
nothing:
ret
; out: edi -> list of the current tab or 0, ZF = 1 if 0
tab_list:
imul edi, [cur_tab], sizeof.TAB
movzx edi, word [tabs + edi + TAB.list]
test edi, edi
ret
; Copy the list at esi to edi unless ZF = 1: the tab has none.
copy_list:
jz @f
mov ecx, sizeof.LIST / 4
rep movsd
@@:
ret
;-------------------------------------------------------------------------------
; Settings
MIN_W = 640 ; fits the longest bottom bar (ru_RU)
MIN_H = 400 ; four graphs are still readable
TICK = 100 ; statistics period, 1/100 s
HIST_LEN = 1024 ; power of 2, the bars of a 4K wide window
MAX_PROCS = 256
; Layout
PAD = 8 ; margins and gaps
TOP = 12 ; above the tabs
TAB_H = 24
AREA_Y = TOP + TAB_H - 1 ; top of the panel, the bottom of the tabs
BTN_H = 24
BTN_PAD = 12 ; text to the sides of a button
CHECK = 14 ; checkbox
ICON = 18 ; icons of @RESHARE
TEXT_CHARS = 63 ; the longest text drawn
TEXT_H = 14 ; 8x16 glyphs look centered as 14 lines
; The window and the text
EVENTS = EVM_REDRAW + EVM_KEY + EVM_BUTTON + EVM_MOUSE + EVM_MOUSE_FILTER
WIN_STYLE = 0x73000000 ; skinned, a caption, coordinates in
; the client area, not filled: drawn
TEXT_UTF8 = 0xB0000000 ; asciiz, UTF-8, 8x16
WND_MAXIMIZED = 1 ; process_information.wnd_state
WND_MINIMIZED = 2
WND_ROLLED_UP = 4
; Lists
COL_W = 112
ID_W = 3*8 + 2*CELL_PAD ; a number: ID, Nº or #, the arrow
KIND_TEXT = 0 ; zero ended text in the row, sorted ascending
KIND_STR = 1 ; pointer to text
KIND_NUM = 2
KIND_PCT = 3 ; from here on sorted down at first
KIND_SIZE = 4
KIND_HEX = 5
KIND_BAR = 6 ; 1/1024 of the cell filled
KIND_ICON = 7 ; in ICONS18
HDR_H = 24 - 2 ; 24 with its lines, as an arrow
ROW_H = 20
SB_W = 24 ; scrollbar, as high as the list
CELL_PAD = 8
; Keys and mouse buttons
KEY_TAB = 9
KEY_ENTER = 13
KEY_SPACE = 32
KEY_ESC = 27
KEY_DOWN = 177 ; the moves in the lists up to
KEY_DEL = 182
KEY_PGUP = 184 ; here
MB_LEFT = 1 ; held
MB_LEFT_DOWN = 0x100 ; pressed, not held at the last event
MB_RIGHT_DOWN = 0x200
; pusha: where it keeps the registers
PUSHA_EDI = 0
PUSHA_EBX = 16
PUSHA_EDX = 20
PUSHA_ECX = 24
PUSHA_EAX = 28
PUSHA_SIZE = 32
; Buttons
BTN_TAB = 2
BTN_SHUTDOWN = 8 ; ids with bit 3 go to the right
BTN_OWN = 16 ; ids from here on belong to the tab
struct TAB ; a row of `tabs`
caption dw ?
list dw ? ; LIST or 0
activate dw ? ; the tab is opened
draw dw ? ; full redraw of the panel
update dw ? ; new statistics
button dw ? ; edx = button id
mouse dw ?
bar dw ? ; the items of its bottom bar
ends
;-------------------------------------------------------------------------------
tabs:
TAB sz_tab_procs, procs_list, nothing, procs_draw, procs_update, procs_button, list_mouse, procs_bar
TAB sz_tab_perf, 0, nothing, perf_draw, perf_draw, perf_cpuid, get_mouse, perf_bar
TAB sz_tab_disks, disks_list, disks_scan, list_draw, disks_update, disks_open, list_mouse, disks_bar
TAB sz_tab_drivers, drivers_list, drivers_scan, list_draw, nothing, drivers_load, list_mouse, drivers_bar
TAB sz_tab_autorun, autorun_list, autorun_load, list_draw, nothing, autorun_toggle, list_mouse, autorun_bar
NTABS = ($ - tabs) / sizeof.TAB
app_info FileInfoBlock SSF_START_APP, 0, 0, 0, 0, 0, 0 ; the parameters, the path
sz_run_path db "/sys/RUN", 0
sz_end_path db "/sys/END", 0
sz_tinfo_path db "/sys/TINFO", 0
icons_name db "ICONS18", 0
box_lib_path db "/sys/lib/box_lib.obj", 0
align 4
box_lib:
import box_lib, \
scrollbar_v_draw, "scrollbar_v_draw", \
scrollbar_v_mouse, "scrollbar_v_mouse", \
check_box_draw2, "check_box_draw2"
sb scrollbar SB_W - 1, 0, 0, AREA_Y, SB_W - 1, 0, 0, 0, 0, 0, 0, 1 ; the sizes - 1:
; box_lib draws a pixel more
cb check_box2 0, 0, PAD, 0, 0, 0x80000000, cb.size_of_str, 0 ; no text: its length
IncludeUGlobals
align 4
sc system_colors ; the button color: the main one
green dd ? ; of the button: run
red dd ? ; and of high values, end
mid dd ? ; the list header, the scrollbar
pinfo process_information
next_tick dd ?
cur_tab dd ?
buttons dd ? ; mouse buttons held at the last event
area_w dd ? ; panel: x = PAD, y = AREA_Y
area_h dd ?
bar_y dd ? ; bottom bar
bar_left dd ?
bar_right dd ? ; left end of the right buttons
icons dd ? ; ICONS18 or 0
icon_buf rd ICON*ICON
text_buf rb TEXT_CHARS * 4 + 1 ; UTF-8 takes up to 4 bytes
macro max_of name, [value] {
common name = 0
forward
if value > name
name = value
end if
}
max_of TAB_DATA, PROCS_DATA, DISKS_DATA, DRIVERS_DATA, AUTORUN_DATA
align 4
tab_data rb TAB_DATA ; data of the open tab
+240
View File
@@ -0,0 +1,240 @@
; The window: its colors, size and place, the panel and the tabs, then the
; panel of the open tab.
draw_window:
mcall SF_REDRAW, SSF_BEGIN_DRAW
mcall SF_STYLE_SETTINGS, SSF_GET_COLORS, sc, sizeof.system_colors
mov eax, [sc.work_button] ; its channels high to low: al, ah, dl
mov edx, eax
shr edx, 16
cmp al, ah
jae @f
xchg al, ah
@@:
cmp al, dl
jae @f
xchg al, dl
@@:
cmp ah, dl
jae @f
xchg ah, dl
@@:
movzx ecx, dl ; blue, green, red
shl ecx, 16
mov cx, ax
mov [sc.work_button], ecx
mov dl, al ; the low ones half the high one
shr dl, 1
movzx ecx, dl ; all low, then green, red high
imul ecx, 0x10101
mov [green], ecx
mov byte [green + 1], al
mov [red], ecx
mov byte [red + 2], al
mov eax, [sc.work] ; between the window and the panel
mov edx, [sc.work_light]
call mix
mov [mid], eax
mcall SF_GET_SCREEN_SIZE ; half the screen, the kernel takes
shr eax, 1 ; it the first time
xchg esi, eax
mcall SF_STYLE_SETTINGS, SSF_GET_SCREEN_AREA
mov edx, esi
shr edx, 16
mov ebp, MIN_W - 1
call .fit
xchg eax, ebx
movzx edx, si
and dh, 0x7F
mov ebp, MIN_H - 1
call .fit
xchg eax, ecx
mov edx, [sc.work]
or edx, WIN_STYLE
mcall SF_CREATE_WINDOW, , , , 0, sz_title
mcall SF_THREAD_INFO, pinfo, -1
test [pinfo.wnd_state], WND_ROLLED_UP
jnz .done
test [pinfo.wnd_state], WND_MAXIMIZED ; the size it gets
jnz .layout
mov edx, [pinfo.box.width] ; not smaller than the minimum
mov esi, [pinfo.box.height]
cmp edx, MIN_W - 1 ; the kernel keeps the size - 1
jae @f
mov edx, MIN_W - 1
@@:
cmp esi, MIN_H - 1
jae @f
mov esi, MIN_H - 1
@@:
cmp edx, [pinfo.box.width]
jne @f
cmp esi, [pinfo.box.height]
je .layout
@@:
mcall SF_CHANGE_WINDOW, -1, -1 ; it sends a new redraw
jmp .done
.layout:
mov eax, [pinfo.client_box.width]
lea ecx, [eax - PAD*2]
mov [area_w], ecx
mov [bar_right], eax
mov [bar_left], PAD
mov eax, [pinfo.client_box.height]
sub eax, PAD + BTN_H
mov [bar_y], eax
sub eax, AREA_Y + PAD
mov [area_h], eax
mov ebx, PAD shl 16
mov bx, word [area_w]
mov ecx, AREA_Y shl 16
mov cx, ax
mov edx, [sc.work_light]
call frame_rect
mov edx, [sc.work] ; around the tabs and the panel: above,
mov ebx, [area_w] ; below with the bar, left, right
add ebx, 2*PAD + 1
mcall SF_DRAW_RECT, , TOP
mov ecx, [bar_y]
sub ecx, PAD
shl ecx, 16
mov cx, 2*PAD + BTN_H + 1
mcall SF_DRAW_RECT
mov ecx, [area_h]
add ecx, (TOP shl 16) + TAB_H - 1
mov ebx, PAD
mcall SF_DRAW_RECT
mov ebx, [area_w]
add ebx, PAD
shl ebx, 16
mov bx, PAD + 1
mcall SF_DRAW_RECT
mov esi, [area_w] ; the tabs as wide as the panel, the
add esi, NTABS - 1 ; room left shared alike
xor ebp, ebp
@@:
call .content
sub esi, eax
inc ebp
cmp ebp, NTABS
jb @b
xchg eax, esi
xor edx, edx
mov ecx, NTABS
div ecx
xchg eax, esi
mov ebx, PAD shl 16
xor ebp, ebp
.tab:
call .content
add eax, esi
cmp ebp, NTABS - 1 ; the last one to the end of the panel
jne @f
mov eax, [area_w]
add eax, PAD
mov edx, ebx
shr edx, 16
sub eax, edx
@@:
mov bx, ax
call .draw
imul eax, ebx, 1 shl 16
lea ebx, [ebx + eax - 1 shl 16] ; neighbours share the border
inc ebp
cmp ebp, NTABS
jb .tab
mov eax, sz_shutdown ; at the right end
mov edx, BTN_SHUTDOWN
mov esi, [sc.work_graph]
call bar_button
imul esi, [cur_tab], sizeof.TAB ; the bar of the tab, then its
movzx esi, word [tabs + esi + TAB.bar] ; panel
call bar_draw
mov eax, TAB.draw
call tab_call
.done:
mcall SF_REDRAW, SSF_END_DRAW
ret
; eax = start shl 16 + end of the work area, edx = the size - 1 wanted,
; ebp = the least; out: eax = start shl 16 + size - 1 of the window in its
; middle
.fit:
cmp edx, ebp
jae @f
mov edx, ebp
@@:
movzx ecx, ax
shr eax, 16
sub ecx, eax
cmp edx, ecx
jb @f
mov edx, ecx
@@:
sub ecx, edx
shr ecx, 1
add eax, ecx
shl eax, 16
mov ax, dx
ret
.draw: ; the caption in the middle
pusha
mov ecx, (TOP shl 16) + TAB_H
mov esi, [sc.work_dark]
cmp ebp, [cur_tab]
jne @f
mov esi, [sc.work_light]
@@:
mov edx, esi
call frame_rect
lea edx, [ebp + BTN_TAB + BT_HIDE + BT_NOFRAME]
mcall SF_DEFINE_BUTTON
call .content
movzx ecx, bx
sub ecx, eax
shr ecx, 1
shl ecx, 16
add ebx, ecx
mov ecx, TOP shl 16
mov eax, [sc.work_text]
jmp bar_text ; as high as the bar items
; ebp = tab; out: edi -> its caption, eax = its width
.content:
imul edi, ebp, sizeof.TAB
movzx edi, word [tabs + edi + TAB.caption]
mov eax, edi
call text_width
ret