From 23c943a20a85e5ee11a276b5ecd0a82821670c78 Mon Sep 17 00:00:00 2001 From: Dilkhush Purohit Date: Sun, 22 Mar 2026 06:43:30 +0530 Subject: [PATCH] add gui implementation of matrix client for kos --- matrix/gui/matrix/Tupfile.lua | 2 + matrix/gui/matrix/gui.inc | 294 ++++++++++++ matrix/gui/matrix/http_client.inc | 757 ++++++++++++++++++++++++++++++ matrix/gui/matrix/matrix.asm | 606 ++++++++++++++++++++++++ matrix/gui/matrix/matrix.ini | 9 + matrix/gui/matrix/mbedtls.inc | 22 + matrix/gui/matrix/textbox.inc | 463 ++++++++++++++++++ matrix/gui/matrix/userparser.inc | 273 +++++++++++ matrix/gui/matrix/window.inc | 227 +++++++++ 9 files changed, 2653 insertions(+) create mode 100644 matrix/gui/matrix/Tupfile.lua create mode 100644 matrix/gui/matrix/gui.inc create mode 100644 matrix/gui/matrix/http_client.inc create mode 100644 matrix/gui/matrix/matrix.asm create mode 100644 matrix/gui/matrix/matrix.ini create mode 100644 matrix/gui/matrix/mbedtls.inc create mode 100644 matrix/gui/matrix/textbox.inc create mode 100644 matrix/gui/matrix/userparser.inc create mode 100644 matrix/gui/matrix/window.inc diff --git a/matrix/gui/matrix/Tupfile.lua b/matrix/gui/matrix/Tupfile.lua new file mode 100644 index 0000000..e9e4095 --- /dev/null +++ b/matrix/gui/matrix/Tupfile.lua @@ -0,0 +1,2 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("matrix.asm", "fasm %f %o " .. tup.getconfig("KPACK_CMD"), "matrix") diff --git a/matrix/gui/matrix/gui.inc b/matrix/gui/matrix/gui.inc new file mode 100644 index 0000000..130c689 --- /dev/null +++ b/matrix/gui/matrix/gui.inc @@ -0,0 +1,294 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +draw_window: ; Completely redraw the window, recalculate all coordinates and sizes + + pusha + +; get system colors + mcall 48, 3, colors, 40 + call set_edit_box_and_scrollbar_colors + + mcall 9, thread_info, -1 ; get information about this thread + mov eax, [thread_info.box.width] ; window xsize + mov ebx, [thread_info.box.height] ; ysize + mov edx, [thread_info.client_box.width] ; work area xsize + mov esi, [thread_info.client_box.height] ; ysize + sub eax, edx + sub ebx, esi + + cmp edx, WIN_MIN_X + jae .x_ok + mov edx, WIN_MIN_X + .x_ok: + mov [xsize], edx + add edx, eax + + cmp esi, WIN_MIN_Y + jae .y_ok + mov esi, WIN_MIN_Y + .y_ok: + mov [ysize], esi + add esi, ebx + mcall 67, -1, -1 ; set the new sizes + + popa + + .dont_resize: + + pusha + + mcall 12, 1 + xor eax, eax ; draw window + mov ebx, WIN_MIN_X+10 + mov ecx, WIN_MIN_Y+30 + mov edx, [colors.work] + add edx, 0x33000000 + mov edi, str_programname + mcall + + test [thread_info.wnd_state], 100b ; skip if window is rolled up + jne .exit + + cmp [window_active], 0 + je .no_window + +; calculate available space for textbox and coordinates for scrollbars + mov eax, [ysize] + sub eax, TOP_Y + INPUTBOX_HEIGHT - 1 + mov [scroll2.y_size], ax + mov [scroll1.y_size], ax + sub eax, 4 + xor edx, edx + mov ecx, FONT_HEIGHT + div ecx + mov [textbox_height], eax + mov [scroll2.cur_area], eax + mov [scroll1.cur_area], eax + + mov eax, [xsize] + sub eax, SCROLLBAR_WIDTH + mov [scroll1.x_pos], ax + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_ROOM + jne @f + sub eax, SCROLLBAR_WIDTH + 2 + @@: + mov [scroll2.x_pos], ax + sub eax, 10 + xor edx, edx + mov ecx, FONT_WIDTH + div ecx + mov [textbox_width], eax + +; recalculate text line breaks (because height/width might have changed..) +; meanwhile, recalculate line number of current line + mov esi, [edi + window.text_print] + mov al, byte[esi] + push eax + mov byte[esi], 0 + push esi + + mov esi, [edi + window.text_start] + call text_insert_newlines + mov [edi + window.text_lines], edx + mov [edi + window.text_scanned], esi + mov [edi + window.text_line_print], edx + + pop esi + pop eax + mov byte[esi], al + +; and redraw the textbox (and scrollbar if needed) + mov [scroll2.all_redraw], 1 + call draw_chat_text + +; Draw userlist if active window is a channel + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_ROOM + jne .not_room + + mov [scroll1.all_redraw], 1 + call draw_user_list + +; draw a vertical separator line when there is no scrollbar + cmp [scroll2.all_redraw], 1 + jne .not_room + + mov ebx, [xsize] + sub ebx, SCROLLBAR_WIDTH + 3 + push bx + shl ebx, 16 + pop bx + mov ecx, [ysize] + add ecx, TOP_Y shl 16 - (INPUTBOX_HEIGHT) + mov edx, [colors.work_graph] + mcall 38 + .not_room: + .no_window: + +; draw editbox + mov eax, [ysize] + sub eax, INPUTBOX_HEIGHT + mov [edit1.top], eax + + mov eax, [xsize] + mov [edit1.width], eax + + invoke edit_box_draw, edit1 + +; draw tabs + call draw_window_tabs + + .exit: + mcall 12, 2 + popa + ret + + + +draw_user_list: + ret + + pusha + + ; Do we need a scrollbar? + mov ebx, [window_active] + + ret + + +draw_window_tabs: + +; Draw horizontal line + + mov ebx, [xsize] + mov edx, [colors.work_graph] + mov ecx, TOP_Y SHL 16 + TOP_Y + mcall 38 + +;; Create the buttons + +; mov eax, 8 +; mov ebx, TAB_WIDTH +; mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT +; mov edx, WINDOW_BTN_START +; mov edi, windows +; .more_btn: +; mov esi, [colors.work] +; cmp [window_active], edi +; jne @f +; ;not esi +; ;and esi, 0x00ffffff +; mov esi, [colors.work_light] +; @@: +; mcall +; inc edx +; add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 +; add edi, sizeof.window +; cmp [edi + window.data_ptr], 0 +; jne .more_btn + +;; Draw the close window button +; mov edi, [window_active] +; cmp [edi + window.type], WINDOWTYPE_SERVER ; don't let the user close server window +; je @f + +;; mov eax, 8 +; mov ebx, [xsize] +; sub ebx, SCROLLBAR_WIDTH +; shl ebx, 16 +; mov bx, SCROLLBAR_WIDTH +; mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT - 1 +; mov edx, WINDOW_BTN_CLOSE +; mov esi, 0x00aa0000 ; red ! +; mcall + +; pusha +; ; write closing cross +; mov ebx, [xsize] +; sub ebx, 9 +; shl ebx, 16 +; add ebx, TOP_SPACE+3 +; mov ecx, 0x80FFFfff +; mov edx, closing_cross +; mcall 4 +; popa +; @@: + +;; Draw the windownames onto the buttons + +; mov eax, 4 +; mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; +; mov esi, MAX_WINDOWS +; mov edi, windows +; .more: +; mov ecx, [colors.work_text] +; test [edi + window.flags], FLAG_UPDATED +; jz @f +; mov ecx, 0x00aa0000 ; RED! +; @@: +; or ecx, 0x80000000 ; ASCIIZ string +; lea edx, [edi + window.name] +; mcall +; add edi, sizeof.window ; get ptr to next window +; cmp [edi + window.data_ptr], 0 +; je .enough +; add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 +; dec esi +; jnz .more +; .enough: + +; ret + + + +highlight_updated_tabs: + mov eax, 4 + mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; + mov ecx, 0x80aa0000 + mov esi, MAX_WINDOWS + mov edi, windows + .more_: + test [edi + window.flags], FLAG_UPDATED + jz .next + lea edx, [edi + window.name] + mcall + .next: + add edi, sizeof.window ; get ptr to next window + cmp [edi + window.data_ptr], 0 + je .enough_ + add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 + dec esi + jnz .more_ + .enough_: + + ret + +set_edit_box_and_scrollbar_colors: + mov eax, [colors.work] + mov [scroll1.bg_color], eax + mov [scroll2.bg_color], eax + + mov eax, [colors.work_button] + mov [scroll1.front_color], eax + mov [scroll2.front_color], eax + + mov eax, [colors.work_text] + mov [scroll1.line_color], eax + mov [scroll2.line_color], eax + + mov [scroll1.type], 0 ; 0 = simple, 1 = skinned + mov [scroll2.type], 0 + + ret diff --git a/matrix/gui/matrix/http_client.inc b/matrix/gui/matrix/http_client.inc new file mode 100644 index 0000000..30214c6 --- /dev/null +++ b/matrix/gui/matrix/http_client.inc @@ -0,0 +1,757 @@ +init_tls: + pusha + DEBUGF 1, 'matrix_connect start\n' + cinvoke mbedtls_init + test eax, eax + jnz .fail_init + + cinvoke mbedtls_net_init, server_fd + cinvoke mbedtls_ssl_init, ssl + cinvoke mbedtls_ssl_config_init, conf + cinvoke mbedtls_x509_crt_init, cacert + + cinvoke mbedtls_ctr_drbg_init, ctr_drbg + cinvoke mbedtls_entropy_init, entropy + cinvoke mbedtls_ctr_drbg_seed, ctr_drbg, [mbedtls_entropy_func], entropy, pers, pers.len + test eax, eax + jnz .fail_seed + + ; for demo purposes, let's just skip it... + ; mov eax, [mbedtls_test_cas_pem_len] + ; cinvoke mbedtls_x509_crt_parse + test eax, eax + ; jnz .fail_parse + + popa + xor eax, eax + ret + +.fail_init: + DEBUGF 1, 'init_tls failed at mbedtls_init with code %d\n', eax + jmp .fail_common +.fail_seed: + DEBUGF 1, 'init_tls failed at drbg_seed with code %d\n', eax + jmp .fail_common +.fail_parse: + DEBUGF 1, 'init_tls failed at crt_parse with code %d\n', eax + jmp .fail_common +.fail: +.fail_common: + popa + mov eax, -1 + ret + +matrix_connect: + ; input: none + ; ret: eax = 0 on success + pusha + DEBUGF 1, 'matrix_connect start\n' + cinvoke mbedtls_net_connect, server_fd, matrix_host, matrix_port, MBEDTLS_NET_PROTO_TCP + test eax, eax + jnz .fail + + cinvoke mbedtls_ssl_config_defaults, conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT + test eax, eax + jnz .fail + + cinvoke mbedtls_ssl_conf_authmode, conf, MBEDTLS_SSL_VERIFY_NONE + ; cinvoke mbedtls_ssl_conf_ca_chain, conf, cacert, 0 + cinvoke mbedtls_ssl_conf_rng, conf, [mbedtls_ctr_drbg_random], ctr_drbg + DEBUGF 1, 'ssl_setup\n' +cinvoke mbedtls_ssl_setup, ssl, conf + test eax, eax + jnz .fail + + DEBUGF 1, 'ssl_set_hostname\n' +cinvoke mbedtls_ssl_set_hostname, ssl, matrix_host + DEBUGF 1, 'ssl_set_bio\n' +cinvoke mbedtls_ssl_set_bio, ssl, server_fd, [mbedtls_net_send], [mbedtls_net_recv], 0 + + DEBUGF 1, 'handshake_loop\n' +.handshake_loop: + cinvoke mbedtls_ssl_handshake, ssl + cmp eax, MBEDTLS_ERR_SSL_WANT_READ + je .handshake_loop + cmp eax, MBEDTLS_ERR_SSL_WANT_WRITE + je .handshake_loop + test eax, eax + jnz .fail + + popa + xor eax, eax + ret +.fail: + popa + mov eax, -1 + ret + +matrix_disconnect: + pusha + DEBUGF 1, 'matrix_connect start\n' + cinvoke mbedtls_ssl_close_notify, ssl + cinvoke mbedtls_net_free, server_fd + cinvoke mbedtls_ssl_free, ssl + cinvoke mbedtls_ssl_config_free, conf + cinvoke mbedtls_x509_crt_free, cacert + cinvoke mbedtls_ctr_drbg_free, ctr_drbg + cinvoke mbedtls_entropy_free, entropy + popa + ret + +; basic string copy +; esi = source +; edi = destination +; returns edi = end of copied string (null terminator not included) +strcpy: + .loop: + lodsb + test al, al + jz .done + stosb + jmp .loop + .done: + ret + +str_json_p1 db '{"type":"m.login.password","user":"', 0 +str_json_p2 db '","password":"', 0 +str_json_p3 db '"}', 0 + +str_http_p1 db 'POST /_matrix/client/v3/login HTTP/1.0', 13, 10 + db 'Host: matrix.org', 13, 10 + db 'Content-Type: application/json', 13, 10 + db 'Content-Length: ', 0 +str_http_p2 db 13, 10, 'Connection: close', 13, 10, 13, 10, 0 + +str_http_logout_p1 db 'POST /_matrix/client/v3/logout HTTP/1.0', 13, 10 + db 'Host: matrix.org', 13, 10 + db 'Authorization: Bearer ', 0 + +str_http_logout_p2 db 13, 10 + db 'Content-Length: 2', 13, 10 + db 'Connection: close', 13, 10, 13, 10 + db '{}', 0 + +str_http_rooms_p1 db 'GET /_matrix/client/v3/joined_rooms HTTP/1.0', 13, 10 + db 'Host: matrix.org', 13, 10 + db 'Authorization: Bearer ', 0 + +str_http_rooms_p2 db 13, 10 + db 'Connection: close', 13, 10, 13, 10, 0 + +str_http_peek_p1 db 'GET /_matrix/client/v3/rooms/', 0 +str_http_peek_p2 db '/messages?dir=b&limit=5 HTTP/1.0', 13, 10 + db 'Host: matrix.org', 13, 10 + db 'Authorization: Bearer ', 0 +str_body_key db '"body":"', 0 + +str_access_token_key db '"access_token":"', 0 + +; substr search +; ebx = text, edi = pattern +; returns eax = pointer to match, or 0 +strstr: + push ebx edi esi edx + test ebx, ebx + jz .not_found + test edi, edi + jz .not_found + .loop: + mov al, byte [ebx] + test al, al + jz .not_found + mov esi, ebx + mov edx, edi + .check: + mov ah, byte [edx] + test ah, ah + jz .found + lodsb + test al, al + jz .not_found + cmp al, ah + jne .next + inc edx + jmp .check + .next: + inc ebx + jmp .loop + .found: + mov eax, ebx + pop edx esi edi ebx + ret + .not_found: + xor eax, eax + pop edx esi edi ebx + ret + +; parse ascii to int +; esi = ascii ptr +; returns eax = int, 0 if it fails +atoi: + push ebx ecx esi + xor eax, eax + xor ecx, ecx +.atoi_loop: + mov cl, byte [esi] + test cl, cl + jz .atoi_done + sub cl, '0' + cmp cl, 9 + ja .atoi_error + imul eax, 10 + add eax, ecx + inc esi + jmp .atoi_loop +.atoi_error: + xor eax, eax +.atoi_done: + pop esi ecx ebx + ret + +; int to string +; eax = number +; edi = output buffer +; returns edi = end of string +itoa: + push ebx ecx edx + mov ecx, 10 + xor ebx, ebx + .div_loop: + xor edx, edx + div ecx + push edx + inc ebx + test eax, eax + jnz .div_loop + .pop_loop: + pop eax + add al, '0' + stosb + dec ebx + jnz .pop_loop + pop edx ecx ebx + ret + +; inputs: +; esi = username +; edx = password +matrix_login_api: + pusha + DEBUGF 1, 'matrix_connect start\n' + + ; build the json + DEBUGF 1, 'building JSON\n' + DEBUGF 1, 'building JSON\n' + mov edi, json_req_buf + push esi + mov esi, str_json_p1 + call strcpy + pop esi + call strcpy + + push esi + mov esi, str_json_p2 + call strcpy + mov esi, edx + call strcpy + mov esi, str_json_p3 + call strcpy + pop esi + + ; find json length + DEBUGF 1, 'calculating JSON len\n' + DEBUGF 1, 'calculating JSON len\n' + mov ebx, edi + sub ebx, json_req_buf ; ebx is content len + + ; build HTTP header + body + DEBUGF 1, 'building HTTP header\n' + DEBUGF 1, 'building HTTP header\n' + mov edi, http_req_buf + mov esi, str_http_p1 + call strcpy + + mov eax, ebx + call itoa + + mov esi, str_http_p2 + call strcpy + + ; append body + DEBUGF 1, 'appending JSON\n' + DEBUGF 1, 'appending JSON\n' + mov esi, json_req_buf + mov ecx, ebx + rep movsb + mov byte [edi], 0 + + ; set buf length + mov ecx, edi + sub ecx, http_req_buf + + ; send block via tls + mov esi, http_req_buf + .send_req: + DEBUGF 1, 'calling ssl_write\n' + DEBUGF 1, 'calling ssl_write\n' + push ecx + cinvoke mbedtls_ssl_write, ssl, esi, ecx + pop ecx + cmp eax, 0 + jle .send_error + add esi, eax + sub ecx, eax + jnz .send_req + + ; drain tls socket + mov edi, http_resp_buf + mov ecx, 32000 ; max buf size + .read_resp: + DEBUGF 1, 'calling ssl_write\n' + DEBUGF 1, 'calling ssl_write\n' + push ecx + DEBUGF 1, 'calling ssl_read\n' + DEBUGF 1, 'calling ssl_read\n' + cinvoke mbedtls_ssl_read, ssl, edi, ecx + pop ecx + cmp eax, MBEDTLS_ERR_SSL_WANT_READ + je .read_resp + cmp eax, MBEDTLS_ERR_SSL_WANT_WRITE + je .read_resp + cmp eax, 0 + jle .read_done + add edi, eax + sub ecx, eax + jnz .read_resp + .read_done: + mov byte [edi], 0 + + ; grab access token + mov ebx, http_resp_buf + mov edi, str_access_token_key + call strstr + test eax, eax + jz .no_token + add eax, 16 ; length of '"access_token":"' + mov esi, eax + mov edi, auth_token + .copy_token: + lodsb + cmp al, '"' + je .token_done + cmp al, 0 + je .token_done + stosb + jmp .copy_token + .token_done: + mov byte [edi], 0 + mov dword [login_status], 1 + + .no_token: + + ; print on gui & dump to board + DEBUGF 1, 'printing response\n' + + ; make sure esi is cool + cld ; cld just because C libs mess with it + mov esi, http_resp_buf + call print_asciiz + + ; line break + mov al, 10 + call print_char + mov al, 13 + call print_char + + popa + xor eax, eax + ret + .send_error: + popa + mov eax, -1 + ret + + +matrix_logout_api: + pusha + + ; build http req + body + DEBUGF 1, 'building logout request\n' + mov edi, http_req_buf + mov esi, str_http_logout_p1 + call strcpy + + mov esi, auth_token + call strcpy + + mov esi, str_http_logout_p2 + call strcpy + + ; set buf length + mov ecx, edi + sub ecx, http_req_buf + + ; send block via tls + mov esi, http_req_buf + .send_req: + DEBUGF 1, 'calling ssl_write\n' + push ecx + cinvoke mbedtls_ssl_write, ssl, esi, ecx + pop ecx + cmp eax, 0 + jle .send_error + add esi, eax + sub ecx, eax + jnz .send_req + + ; drain tls socket + mov edi, http_resp_buf + mov ecx, 32000 + .read_resp: + push ecx + DEBUGF 1, 'calling ssl_read\n' + cinvoke mbedtls_ssl_read, ssl, edi, ecx + pop ecx + cmp eax, MBEDTLS_ERR_SSL_WANT_READ + je .read_resp + cmp eax, MBEDTLS_ERR_SSL_WANT_WRITE + je .read_resp + cmp eax, 0 + jle .read_done + add edi, eax + sub ecx, eax + jnz .read_resp + .read_done: + mov byte [edi], 0 + + ; clear token + mov byte [auth_token], 0 + + ; print to gui + DEBUGF 1, 'printing logout response\n' + cld + mov esi, http_resp_buf + call print_asciiz + + ; line break + mov al, 10 + call print_char + mov al, 13 + call print_char + + popa + xor eax, eax + ret + .send_error: + popa + mov eax, -1 + ret + + +matrix_get_rooms_api: + pusha + ; almost same steps as other reqs + ; build http req + DEBUGF 1, 'building rooms request\n' + mov edi, http_req_buf + mov esi, str_http_rooms_p1 + call strcpy + + mov esi, auth_token + call strcpy + + mov esi, str_http_rooms_p2 + call strcpy + + ; set buf length + mov ecx, edi + sub ecx, http_req_buf + + ; send block via tls + mov esi, http_req_buf + .send_req: + DEBUGF 1, 'calling ssl_write\n' + push ecx + cinvoke mbedtls_ssl_write, ssl, esi, ecx + pop ecx + cmp eax, 0 + jle .send_error + add esi, eax + sub ecx, eax + jnz .send_req + + ; drain tls socket + mov edi, http_resp_buf + mov ecx, 32000 + .read_resp: + push ecx + DEBUGF 1, 'calling ssl_read\n' + cinvoke mbedtls_ssl_read, ssl, edi, ecx + pop ecx + cmp eax, MBEDTLS_ERR_SSL_WANT_READ + je .read_resp + cmp eax, MBEDTLS_ERR_SSL_WANT_WRITE + je .read_resp + cmp eax, 0 + jle .read_done + add edi, eax + sub ecx, eax + jnz .read_resp + .read_done: + mov byte [edi], 0 + + ; parse rooms + DEBUGF 1, 'parsing rooms response\n' + mov ebx, http_resp_buf + mov edi, str_joined_rooms_key + call strstr + test eax, eax + jz .no_rooms + + add eax, 16 ; skip '"joined_rooms":[' + mov esi, eax + + mov dword [room_count], 0 + .find_room: + ; find next quote + mov ebx, esi + mov edi, str_quote + call strstr + test eax, eax + jz .no_rooms + + add eax, 1 + mov esi, eax + ; esi points to room_id + + ; find end quote + mov ebx, esi + mov edi, str_quote + call strstr + test eax, eax + jz .no_rooms + + ; cap the room id string + mov byte [eax], 0 + mov edx, eax + add edx, 1 ; edx points to after quote + + ; print room counter + inc dword [room_count] + push eax + push edx + push esi ; stash ptr + + mov eax, [room_count] + mov edi, itoa_buf + call itoa + mov byte [edi], 0 + mov esi, itoa_buf + call print_asciiz + mov al, '.' + call print_char + mov al, ' ' + call print_char + + pop esi ; pop back + pop edx + pop eax + + ; copy room id into safe chunk + mov ecx, [room_count] + cmp ecx, 64 + jg .limit_reached + dec ecx + + pusha + mov eax, ecx + imul eax, 128 + add eax, room_ids_buf + mov edi, eax + + ; esi already points to null-terminated room id from the JSON + call strcpy + mov byte [edi], 0 + + ; keep a pointer array to the safe buffer blocks + mov eax, ecx + imul eax, 128 + add eax, room_ids_buf + imul ecx, 4 + add ecx, room_pointers + mov dword [ecx], eax + popa + + call print_asciiz + + mov al, 10 + call print_char + mov al, 13 + call print_char + + mov esi, edx + jmp .find_room + + .limit_reached: + .no_rooms: + mov al, 10 + call print_char + mov al, 13 + call print_char + + popa + xor eax, eax + ret + .send_error: + popa + mov eax, -1 + ret + + +; eax = room number requested +matrix_peek_api: + pusha + + cmp eax, 0 + jle .peek_end + cmp eax, [room_count] + jg .peek_end + + ; point at the room id + dec eax + imul eax, 4 + add eax, room_pointers + mov ebx, dword [eax] ; ebx is our room id ptr + + ; then again build http req + DEBUGF 1, 'building peek request\n' + mov edi, http_req_buf + + mov esi, str_http_peek_p1 + call strcpy + + mov esi, ebx + call strcpy + + mov esi, str_http_peek_p2 + call strcpy + + mov esi, auth_token + call strcpy + + mov esi, str_http_rooms_p2 + call strcpy + + ; set buf length + mov ecx, edi + sub ecx, http_req_buf + + ; send block via tls + mov esi, http_req_buf + .send_req: + DEBUGF 1, 'calling ssl_write\n' + push ecx + cinvoke mbedtls_ssl_write, ssl, esi, ecx + pop ecx + cmp eax, 0 + jle .send_error_peek + add esi, eax + sub ecx, eax + jnz .send_req + + ; drain tls socket + mov edi, http_resp_buf + mov ecx, 32000 + .read_resp: + push ecx + DEBUGF 1, 'calling ssl_read_peek\n' + cinvoke mbedtls_ssl_read, ssl, edi, ecx + pop ecx + cmp eax, MBEDTLS_ERR_SSL_WANT_READ + je .read_resp + cmp eax, MBEDTLS_ERR_SSL_WANT_WRITE + je .read_resp + cmp eax, 0 + jle .read_done + add edi, eax + sub ecx, eax + jnz .read_resp + .read_done: + mov byte [edi], 0 + + ; parse msgs + DEBUGF 1, 'parsing peek response\n' + + mov dword [msg_count], 0 + mov esi, http_resp_buf + .find_msg: + mov ecx, [msg_count] + cmp ecx, 5 + jge .print_msgs + + mov ebx, esi + mov edi, str_body_key + call strstr + test eax, eax + jz .print_msgs + + add eax, 8 ; skip '"body":"' + mov esi, eax + + ; find end quote + mov ebx, esi + mov edi, str_quote + call strstr + test eax, eax + jz .print_msgs + + mov byte [eax], 0 ; null terminate message body + mov edx, eax + add edx, 1 + + ; old to new msg pointer array + mov ecx, [msg_count] + imul ecx, 4 + add ecx, msg_pointers + mov dword [ecx], esi + inc dword [msg_count] + + mov esi, edx + jmp .find_msg + + .print_msgs: + mov ecx, [msg_count] + test ecx, ecx + jz .peek_end + + .print_loop: + dec ecx + + push ecx + imul ecx, 4 + add ecx, msg_pointers + mov esi, dword [ecx] + + mov al, '>' + call print_char + mov al, ' ' + call print_char + + call print_asciiz + mov al, 10 + call print_char + + pop ecx + test ecx, ecx + jg .print_loop + + .no_more_msgs: + .peek_end: + popa + xor eax, eax + ret + + .send_error_peek: + popa + mov eax, -1 + ret diff --git a/matrix/gui/matrix/matrix.asm b/matrix/gui/matrix/matrix.asm new file mode 100644 index 0000000..bd675ca --- /dev/null +++ b/matrix/gui/matrix/matrix.asm @@ -0,0 +1,606 @@ +MAX_SERVER_NAME = 256 + +version equ '0.1b' + +; connection status +STATUS_DISCONNECTED = 0 +STATUS_RESOLVING = 1 +STATUS_CONNECTING = 2 +STATUS_CONNECTED = 3 +STATUS_LOGGED_IN = 4 + +; window flags +FLAG_UPDATED = 1 shl 0 +FLAG_RECEIVING_NAMES = 1 shl 1 +FLAG_SCROLL_LOW = 1 shl 2 + +; window types +WINDOWTYPE_NONE = 0 +WINDOWTYPE_SERVER = 1 +WINDOWTYPE_ROOM = 2 +WINDOWTYPE_CHAT = 3 +WINDOWTYPE_LIST = 4 +WINDOWTYPE_DCC = 5 + + +; settings +USERCMD_MAX_SIZE = 400 + +WIN_MIN_X = 600 +WIN_MIN_Y = 350 + +TEXT_X = 2 +TEXT_Y = TOP_Y + 2 + +TOP_SPACE = 0 +TAB_HEIGHT = 0 +TAB_WIDTH = 120 +TAB_SPACE = 5 +TOP_Y = TOP_SPACE+ TAB_HEIGHT +INPUTBOX_HEIGHT = 15 + +MAX_WINDOWS = 20 +TEXT_BUFFERSIZE = 1024*1024 + +MAX_USERNAME_LEN = 32 +MAX_REAL_LEN = 32 ; realname +QUIT_MSG_LEN = 250 + + +MAX_COMMAND_LEN = 512 + +PACKETBUF_SIZE = 1024 +PATH_SIZE = 1024 +PARAM_SIZE = 1024 + + +TIMESTAMP = 0 ; 3 = hh:mm:ss, 2 = hh:mm, 0 = no timestamp + +MAX_WINDOWNAME_LEN = 256 + +WINDOW_BTN_START = 100 +WINDOW_BTN_CLOSE = 2 +WINDOW_BTN_LIST = 3 + +SCROLLBAR_WIDTH = 14 + +FONT_WIDTH = 8 +FONT_HEIGHT = 16 + +format binary as "" + +use32 + + org 0x0 + + db 'MENUET01' ; 8 byte id + dd 1 ; header version + dd START ; program start + dd I_END ; program image size + dd IM_END+65536 ; memory size + dd IM_END+65536 ; stack top + dd param + dd path + +include "../../macros.inc" +include "../../proc32.inc" +__DEBUG__ = 1 +__DEBUG_LEVEL__ = 1 +include "../../debug-fdo.inc" +include "../../dll.inc" +include "../../network.inc" +include "../../struct.inc" +include "../../develop/libraries/box_lib/trunk/box_lib.mac" + +struct window + data_ptr dd ? + flags db ? + type db ? + name rb MAX_WINDOWNAME_LEN + text_start dd ? + text_print dd ? + text_write dd ? + text_scanned dd ? + text_lines dd ? + text_line_print dd ? + text_end dd ? +ends + +struct window_data + text rb TEXT_BUFFERSIZE +ends + +include "window.inc" +include "userparser.inc" +include "gui.inc" +include "http_client.inc" +include "textbox.inc" +include "mbedtls.inc" + + + + +START: + + mcall 68, 11 ; init heap + +; wanted events + mcall 40, EVM_REDRAW+EVM_KEY+EVM_BUTTON+EVM_STACK+EVM_MOUSE+EVM_MOUSE_FILTER + +; load libraries + stdcall dll.Load, @IMPORT + test eax, eax + jnz exit + +; get settings ini path + mov edi, path ; strlen + xor al, al + mov ecx, PATH_SIZE + repne scasb + dec edi + mov eax, '.ini' + stosd + xor al, al + stosb + +; zero out window buff + mov edi, windows + mov ecx, (sizeof.window*MAX_WINDOWS+3)/4 + xor eax, eax + rep stosd + + + +; alloc window + mov ebx, windows + call window_create_textbox + test eax, eax + jz exit + mov [ebx + window.type], WINDOWTYPE_SERVER + +; suck in settings + invoke ini.get_str, path, str_user, str_username, user_name, MAX_USERNAME_LEN, default_username + invoke ini.get_str, path, str_user, str_real, user_real_name, MAX_REAL_LEN, default_real + invoke ini.get_str, path, str_user, str_quitmsg, quit_msg, 250, default_quit + +; echo welcome + mov esi, str_welcome + call print_asciiz + +; check for param url + cmp byte[param], 0 + je @f + mov esi, param + mov ecx, PARAM_SIZE +; call cmd_usr_server.now + @@: + +redraw: + call draw_window + +mainloop: + mcall 10 ; event loop + + dec eax + jz redraw + + dec eax + jz main_window_key + + dec eax + jz button + + cmp al, 3 + je mouse + +; call process_network_event + + mov edi, [window_active] + test [edi + window.flags], FLAG_UPDATED + jz .no_update + call draw_chat_text + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_ROOM + jne .no_update + call draw_user_list + .no_update: + call highlight_updated_tabs + + jmp mainloop + +button: + + mcall 17 ; get id + ror eax, 8 + + cmp ax, 1 ; kill + je quit + + cmp ax, WINDOW_BTN_CLOSE + jne @f +; call cmd_usr_close_window + jmp mainloop + + @@: + + + @@: + sub ax, WINDOW_BTN_START + jb quit + + cmp ax, MAX_WINDOWS + ja quit + +; save scroll pos + push [scroll1.position] + mov edx, [window_active] + add esp, 4 + +; switch tab + mov dx, sizeof.window + mul dx + shl edx, 16 + mov dx, ax + add edx, windows + cmp [edx + window.type], WINDOWTYPE_NONE + je quit + mov [window_active], edx + + push [edx + window.text_line_print] + pop [scroll2.position] + + push 0 + pop [scroll1.position] + + call draw_window + jmp mainloop + +quit: + cmp [socketnum], 0 + je @f + mov esi, quit_msg +; call quit_server + @@: + +exit: + +; nuke windows + call window_close_all + +; zero connection strings from memory + xor eax, eax + mov edi, homeserver_url + mov ecx, MAX_SERVER_NAME + rep stosb + + mov edi, user_name + mov ecx, MAX_USERNAME_LEN + rep stosb + + mov edi, user_real_name + mov ecx, MAX_REAL_LEN + rep stosb + + mov edi, sockaddr1 + mov ecx, SOCKADDR1_SIZE + rep stosb + + mcall -1 + + + +main_window_key: + + mcall 2 + + push dword edit1 + call [edit_box_key] + +; cmp ah, 178 +; jne .no_up +; +; jmp mainloop +; +; +; .no_up: +; cmp ah, 177 +; jne .no_down +; +; jmp mainloop +; +; .no_down: + cmp ah, 13 ; enter + jne no_send2 + + call user_parser + + mov eax, [edit1.size] + + mov [edit1.size], 0 + mov [edit1.pos], 0 + + invoke edit_box_draw, edit1 + + call draw_chat_text + + jmp mainloop + no_send2: + + jmp mainloop + +mouse: + invoke edit_box_mouse, edit1 + +; mcall 37, 7 +; movsx eax, ax +; add [scroll2.position], eax + +; TODO: check if scrollbar is active? + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_ROOM + jne @f + push [scroll1.position] + invoke scrollbar_mouse, scroll1 + pop eax + cmp eax, [scroll1.position] ; did the scrollbar move? + je @f + call draw_user_list + @@: + +; TODO: check if scrollbar is active? + mov edi, [window_active] + mov eax, [edi + window.text_lines] + cmp eax, [textbox_height] + jbe @f + invoke scrollbar_mouse, scroll2 + mov edi, [window_active] + and [edi+window.flags], not FLAG_SCROLL_LOW + mov edx, [scroll2.position] + add edx, [scroll2.cur_area] + sub edx, [scroll2.max_area] + jne .not_low + or [edi+window.flags], FLAG_SCROLL_LOW + .not_low: + mov edx, [scroll2.position] + sub edx, [edi + window.text_line_print] + je @f + call draw_chat_text.scroll_to_pos + @@: + + jmp mainloop + + +; data seg + +encoding_text: + + + + + + + +str_help db 'The following commands are available:', 10 + db '/help : show all commands', 10 + db '/login : login to matrix', 10 + db '/rooms : show rooms', 10 + db '/peek : show last messages of a room', 10 + db '/logout : logout from matrix', 10 + db 10, 0 + +str_logged_in db 'User logged in.', 10, 0 +str_tls_err db 'TLS init failed.', 10, 0 +str_conn_err db 'TLS connection failed.', 10, 0 +str_not_logged_in db 'Error: You are not logged in. Use /login first.', 10, 0 +str_rooms_msg db '1: !roomA:matrix.org', 10, '2: !roomB:matrix.org', 10, 0 +str_peek_usage_err db 'Usage: /peek ', 10, 0 +str_peek_bounds_err db 'Error: Invalid room number.', 10, 0 +str_logout_msg db 'Logged out.', 10, 0 +str_invalid_cmd db 'Invalid command. Type /help for a list of commands.', 10, 0 +str_welcome db 'Welcome to KolibriOS Matrix client ', version, 10 + db 'Type /help for help', 10, 0 + +str_programname db 'Matrix client ', version, 0 + +cmd_help db '/help', 0 +cmd_login db '/login', 0 +cmd_rooms db '/rooms', 0 +cmd_peek db '/peek', 0 +cmd_logout db '/logout', 0 + +str_user db 'user', 0 +str_username db 'username', 0 +str_real db 'realname', 0 +str_email db 'email', 0 +str_quitmsg db 'quitmsg', 0 + +default_username db 'makora', 0 +default_real db 'dilkhush', 0 +default_quit db 'KolibriOS forever!', 0 + +closing_cross db 'x',0 + +chat_colors dd 0xffffff ; 0 white + dd 0x000000 ; 1 black + dd 0x00007f ; 2 blue (navy) + dd 0x009300 ; 3 green + dd 0xff0000 ; 4 red + dd 0x7f0000 ; 5 brown (maroon) + dd 0x9c009c ; 6 purple + dd 0xfc7f00 ; 7 olive + dd 0xffff00 ; 8 yellow + dd 0x00fc00 ; 9 light green + dd 0x009393 ; 10 teal + dd 0x00ffff ; 11 cyan + dd 0x0000fc ; 12 royal blue + dd 0xff00ff ; 13 pink + dd 0x7f7f7f ; 14 grey + dd 0xd4d0c4 ; 15 light grey (silver) + +sockaddr1: + dw AF_INET4 +.port dw 0x0b1a ; 6667 FIXMEEEEEE +.ip dd 0 + rb 10 + +SOCKADDR1_SIZE = 18 + +status dd STATUS_DISCONNECTED + +window_active dd windows +window_print dd windows + +cmd_remaining dd 0 + +align 4 + + + ; width, left, top +edit1 edit_box 0, 0, 0, 0xffffff, 0x6f9480, 0, 0, 0x000000, USERCMD_MAX_SIZE, input_text, mouse_dd, ed_always_focus, 0, 0 + ; xsize, xpos, ysize, ypos, btn_height, max, cur, pos, bgcol, frcol, linecol +scroll1 scrollbar SCROLLBAR_WIDTH, 0, 0, TOP_Y, SCROLLBAR_WIDTH, 0, 0, 0, 0, 0, 0, 1 +scroll2 scrollbar SCROLLBAR_WIDTH, 0, 0, TOP_Y, SCROLLBAR_WIDTH, 0, 0, 0, 0, 0, 0, 1 + +input_text db 0 + rb MAX_COMMAND_LEN + + +@IMPORT: + + + +library network, 'network.obj',\ + libini, 'libini.obj',\ + boxlib, 'box_lib.obj',\ + lib_mbedtls, '/hd0/1/NETWORK/MBEDTLS.OBJ' + +import network,\ + getaddrinfo, 'getaddrinfo',\ + freeaddrinfo, 'freeaddrinfo',\ + inet_ntoa, 'inet_ntoa' + +import libini,\ + ini.get_str, 'ini_get_str',\ + ini.get_int, 'ini_get_int' + +import boxlib,\ + edit_box_draw, 'edit_box_draw',\ + edit_box_key, 'edit_box_key',\ + edit_box_mouse, 'edit_box_mouse',\ + scrollbar_draw, 'scrollbar_v_draw',\ + scrollbar_mouse,'scrollbar_v_mouse' + +import lib_mbedtls, \ +mbedtls_init , 'mbedtls_init' ,\ +mbedtls_strerror , 'mbedtls_strerror' ,\ +mbedtls_test_cas_pem , 'mbedtls_test_cas_pem' ,\ +mbedtls_test_cas_pem_len , 'mbedtls_test_cas_pem_len' ,\ +mbedtls_x509_crt_free , 'mbedtls_x509_crt_free' ,\ +mbedtls_x509_crt_init , 'mbedtls_x509_crt_init' ,\ +mbedtls_x509_crt_parse , 'mbedtls_x509_crt_parse' ,\ +mbedtls_x509_crt_verify_info , 'mbedtls_x509_crt_verify_info' ,\ +mbedtls_ctr_drbg_free , 'mbedtls_ctr_drbg_free' ,\ +mbedtls_ctr_drbg_init , 'mbedtls_ctr_drbg_init' ,\ +mbedtls_ctr_drbg_random , 'mbedtls_ctr_drbg_random' ,\ +mbedtls_ctr_drbg_seed , 'mbedtls_ctr_drbg_seed' ,\ +mbedtls_debug_set_threshold , 'mbedtls_debug_set_threshold' ,\ +mbedtls_entropy_free , 'mbedtls_entropy_free' ,\ +mbedtls_entropy_func , 'mbedtls_entropy_func' ,\ +mbedtls_entropy_init , 'mbedtls_entropy_init' ,\ +mbedtls_net_connect , 'mbedtls_net_connect' ,\ +mbedtls_net_free , 'mbedtls_net_free' ,\ +mbedtls_net_init , 'mbedtls_net_init' ,\ +mbedtls_net_recv , 'mbedtls_net_recv' ,\ +mbedtls_net_send , 'mbedtls_net_send' ,\ +mbedtls_ssl_close_notify , 'mbedtls_ssl_close_notify' ,\ +mbedtls_ssl_conf_authmode , 'mbedtls_ssl_conf_authmode' ,\ +mbedtls_ssl_conf_ca_chain , 'mbedtls_ssl_conf_ca_chain' ,\ +mbedtls_ssl_conf_dbg , 'mbedtls_ssl_conf_dbg' ,\ +mbedtls_ssl_config_defaults , 'mbedtls_ssl_config_defaults' ,\ +mbedtls_ssl_config_free , 'mbedtls_ssl_config_free' ,\ +mbedtls_ssl_config_init , 'mbedtls_ssl_config_init' ,\ +mbedtls_ssl_conf_rng , 'mbedtls_ssl_conf_rng' ,\ +mbedtls_ssl_free , 'mbedtls_ssl_free' ,\ +mbedtls_ssl_get_verify_result , 'mbedtls_ssl_get_verify_result' ,\ +mbedtls_ssl_handshake , 'mbedtls_ssl_handshake' ,\ +mbedtls_ssl_init , 'mbedtls_ssl_init' ,\ +mbedtls_ssl_read , 'mbedtls_ssl_read' ,\ +mbedtls_ssl_set_bio , 'mbedtls_ssl_set_bio' ,\ +mbedtls_ssl_set_hostname , 'mbedtls_ssl_set_hostname' ,\ +mbedtls_ssl_setup , 'mbedtls_ssl_setup' ,\ +mbedtls_ssl_write , 'mbedtls_ssl_write' + + + + + + + + +cmd_args dd 0 +matrix_host db "matrix.org", 0 +matrix_port db "443", 0 +pers db "matrix_client" + .len = $ - pers +login_status dd 0 + + +str_joined_rooms_key db '"joined_rooms":[', 0 +str_quote db '"', 0 +include_debug_strings +I_END: + +align 4 +server_fd rb 64 +align 4 +ssl rb 65536 +align 4 +conf rb 32768 +align 4 +cacert rb 16384 +align 4 +ctr_drbg rb 8192 +align 4 +entropy rb 8192 +align 4 +json_req_buf rb 2048 +http_req_buf rb 4096 +http_resp_buf rb 32768 +auth_token rb 2048 +current_room rb 256 +current_user rb 256 +user_command rb MAX_COMMAND_LEN*4 +.size dd ? +utf8_bytes_rest dd ? +utf8_char dd ? +packetbuf rb PACKETBUF_SIZE +path rb PATH_SIZE +param rb PARAM_SIZE +thread_info process_information +xsize dd ? +ysize dd ? +mouse_dd dd ? +textbox_height dd ? +textbox_width dd ? +colors system_colors +homeserver_url rb MAX_SERVER_NAME +socketnum dd ? +user_name rb MAX_USERNAME_LEN +user_real_name rb MAX_REAL_LEN +quit_msg rb QUIT_MSG_LEN +windows rb MAX_WINDOWS*sizeof.window + + + + + + + +itoa_buf rb 16 +room_count dd ? +room_pointers rb 1024 +room_ids_buf rb 8192 +msg_count dd ? +msg_pointers rb 32 + +IM_END: diff --git a/matrix/gui/matrix/matrix.ini b/matrix/gui/matrix/matrix.ini new file mode 100644 index 0000000..cdaa6ca --- /dev/null +++ b/matrix/gui/matrix/matrix.ini @@ -0,0 +1,9 @@ +[user] +nick = makora +realname = dilkhush +partmsg = KolibriOS forever! + +[colors] +action1 = 0x000000aa +action2 = 0x0000aa00 +action3 = 0x00aa0000 \ No newline at end of file diff --git a/matrix/gui/matrix/mbedtls.inc b/matrix/gui/matrix/mbedtls.inc new file mode 100644 index 0000000..c580b5f --- /dev/null +++ b/matrix/gui/matrix/mbedtls.inc @@ -0,0 +1,22 @@ +macro mbedtls_net_context [args] { common match any,args \{ rb 4 \} match ,args \{ rb 4 \} } +macro mbedtls_entropy_context [args] { common match any,args \{ rb 256 \} match ,args \{ rb 256 \} } +macro mbedtls_ctr_drbg_context [args] { common match any,args \{ rb 352 \} match ,args \{ rb 352 \} } +macro mbedtls_ssl_context [args] { common match any,args \{ rb 600 \} match ,args \{ rb 600 \} } +macro mbedtls_ssl_config [args] { common match any,args \{ rb 360 \} match ,args \{ rb 360 \} } +macro mbedtls_x509_crt [args] { common match any,args \{ rb 1320 \} match ,args \{ rb 1320 \} } + +MBEDTLS_ERR_NET_SOCKET_FAILED = -0x0042 +MBEDTLS_ERR_NET_CONNECT_FAILED = -0x0044 +MBEDTLS_ERR_SSL_WANT_READ = -0x6900 +MBEDTLS_ERR_SSL_WANT_WRITE = -0x6880 + +MBEDTLS_NET_PROTO_TCP = 0 +MBEDTLS_NET_PROTO_UDP = 1 +MBEDTLS_SSL_IS_CLIENT = 0 +MBEDTLS_SSL_IS_SERVER = 1 +MBEDTLS_SSL_TRANSPORT_STREAM = 0 +MBEDTLS_SSL_TRANSPORT_DATAGRAM = 1 +MBEDTLS_SSL_PRESET_DEFAULT = 0 +MBEDTLS_SSL_VERIFY_NONE = 0 +MBEDTLS_SSL_VERIFY_OPTIONAL = 1 +MBEDTLS_SSL_VERIFY_REQUIRED = 2 diff --git a/matrix/gui/matrix/textbox.inc b/matrix/gui/matrix/textbox.inc new file mode 100644 index 0000000..3c85349 --- /dev/null +++ b/matrix/gui/matrix/textbox.inc @@ -0,0 +1,463 @@ +text_insert_newlines: ; esi = ASCIIZ string + + xor edx, edx ; number of lines of text + cmp byte[esi], 0 + je .done + .next_line: + xor ebx, ebx + mov ecx, [textbox_width] + inc ecx + .more: + dec ecx + jz .end_of_line + .next_byte: + lodsb ; get one character of the string + test al, al ; end of string? + jz .almost_done + cmp al, ' ' ; it's a space! remember its position + je .space + cmp al, 13 ; we already inserted a newline once, make it a space again + je .soft_nl + cmp al, 10 ; it's a newline, continue onto the next line + je .newline + and al, 0xc0 ; Is it a multi byte UTF8 char? + cmp al, 0x80 + je .next_byte + jmp .more + .newline: + inc edx + jmp .next_line + .soft_nl: + mov byte[esi-1], ' ' + .space: + mov ebx, esi ; last detected space + jmp .more + .end_of_line: + inc edx + test ebx, ebx ; did we detect any spaces on this line? + jz .next_line ; no: just continue onto the next line + mov byte[ebx-1], 13 ; yes: replace last space on line with a soft newline + mov esi, ebx ; and continue parsing just after last space + jmp .next_line ; + .almost_done: + dec esi + .done: + + ret + + +;---------------------------------- +; scan untill next line is reached +; +; When you set the direction flag before calling, you can also scan for previous line! +; IN: esi +; OUT: esi +;---------------------------------- +text_nextline: + + mov ecx, [textbox_width] + .loop: + lodsb + test al, al + jz .done + cmp al, 10 + je .done + cmp al, 13 + je .done + and al, 0xc0 + cmp al, 0x80 + je .loop ; This byte is the second, third or fourth byte of a multi-byte UTF8 char + dec ecx + jnz .loop + .done: + ret + +;---------------------------------- +; print string +; +; IN: esi = ptr to string +; bl = char which marks end of string +; OUT: esi = ptr to end of str +;---------------------------------- +print_string: + + push eax + .loop: + lodsb + cmp al, bl + je .done + cmp al, 13 + je .loop + test al, al + jz .done + call print_char + jmp .loop + .done: + pop eax + + ret + + +;---------------------------------- +; print ASCIIZ string +; +; IN: esi = ptr to ASCIIZ string +; OUT: esi = ptr to end of str +;---------------------------------- +print_asciiz: + + push eax + .loop: + lodsb + test al, al + jz .done + call print_char + jmp .loop + .done: + pop eax + + ret + + +;---------------------------------- +; print character +; +; IN: al = char to print +; OUT: / +;---------------------------------- +print_char: + + push esi edi + mov esi, [window_print] + mov edi, [esi + window.text_write] + stosb + cmp edi, [esi + window.text_end] + jae .uh_ow + mov [esi + window.text_write], edi + .continue: + or [esi + window.flags], FLAG_UPDATED + pop edi esi + + ret + + .uh_ow: + pusha + mov edi, [esi + window.text_start] + mov [esi + window.text_print], edi + lea esi, [edi + TEXT_BUFFERSIZE/2] + call text_nextline + mov ecx, TEXT_BUFFERSIZE/8 + rep movsd + mov esi, edi + call text_insert_newlines + + mov ebx, [window_print] + mov [ebx + window.text_lines], edx + mov [ebx + window.text_scanned], esi + mov [ebx + window.text_write], esi + mov [ebx + window.text_line_print], 0 + popa + + jmp .continue + + + +;----------------------------------------------- +; Draw text of the current window to the screen +; +; IN: / +; OUT: / +;----------------------------------------------- +draw_chat_text: + + mov edi, [window_active] + and [edi + window.flags], not FLAG_UPDATED ; clear the 'window is updated' flag + +; Scan new text for newlines + mov esi, [edi + window.text_scanned] + call text_insert_newlines + add [edi + window.text_lines], edx + mov [edi + window.text_scanned], esi + +; Is scrollbar at lowest position? + test [edi + window.flags], FLAG_SCROLL_LOW + jnz .yesscroll ; Yes + cmp [scroll2.all_redraw], 1 ; No + jnz .noscroll + mov edx, [textbox_height] + add edx, [edi + window.text_line_print] + cmp edx, [edi + window.text_lines] + jl .noscroll + .yesscroll: +; Scrollbar was at lowest position, scroll down automatically when new text arrived. + mov edx, [edi + window.text_lines] + sub edx, [textbox_height] + jg @f + mov [edi + window.text_line_print], 0 + jmp .noscroll ; There are less lines of text than fit into the window, dont scroll.. + @@: + sub edx, [edi + window.text_line_print] + je .noscroll ; We are already at the bottom pos, dont scroll.. + .scroll_to_pos: ; edx = number of lines to go up/down (flags must indicate direction) + pushf + add [edi + window.text_line_print], edx + mov esi, [edi + window.text_print] + popf + jg .loop_forward + std ; set direction flag so we can scan backwards + dec esi + dec esi ; move our cursor just in front of newline, for scanning backwards + .loop_backward: + call text_nextline + inc edx + jnz .loop_backward + inc esi + inc esi ; move the cursor just after last newline + cld + jmp .ok + + .loop_forward: + call text_nextline + dec edx + jnz .loop_forward + .ok: + mov [edi + window.text_print], esi + .noscroll: + +; Update and draw scrollbar when nescessary + mov edx, [edi + window.text_lines] + cmp edx, [textbox_height] + jbe .scroll_done + + mov [scroll2.max_area], edx + mov eax, [edi + window.text_line_print] + mov [scroll2.position], eax + + push dword scroll2 ; redraw scrollbar + call [scrollbar_draw] + mov [scroll2.all_redraw], 0 ; next time, dont redraw it completely + .scroll_done: + +; Calculate start offset coordinates (align text to bottom) + mov ebx, [textbox_height] + sub ebx, [edi + window.text_lines] + jb .no_offset + imul ebx, FONT_HEIGHT + push [edi + window.text_start] + pop [edi + window.text_print] + jmp .draw_text + .no_offset: + xor ebx, ebx + .draw_text: + +; Prepare to actually draw some text + add ebx, TEXT_X shl 16 + TEXT_Y ; text coordinates + mov ecx, [colors.work_text] ; default text color + or ecx, 0x30000000 + mov edx, [edi + window.text_print] ; start of text to print + +; Scan backwards on line for color escape codes + mov esi, edx + push edx + std + @@: + lodsb + cmp al, 0 ; end of text + je @f + cmp al, 10 ; hard newline + je @f + cmp al, 3 ; chat escape code + jne @b + + cld + lea edx, [esi+2] + call dec_to_esi + jz @f + mov ecx, [chat_colors + 4*esi] + or ecx, 0x30000000 ; UTF-8 text + + cmp byte[edx], ',' ; background color? + jne @f + inc edx + call dec_to_esi + jz @f + mov edi, [chat_colors + 4*esi] + or ecx, 0x40000000 ; enable background color + @@: + cld + + pop edx + mov eax, [textbox_height] ; max number of lines to draw + .drawloop: + cmp byte[edx], 0 + je .end_of_text + +; Clear one row of characters + pusha + mov cx, bx + shl ecx, 16 + mov cx, FONT_HEIGHT + mov ebx, TEXT_X shl 16 + mov bx, word[textbox_width] + imul bx, FONT_WIDTH + mov edx, [colors.work] + mcall 13 ; draw rectangle + popa + + push eax + .line: + cmp byte[edx], 0x20 + jae .printable + cmp byte[edx], 0 + jne @f + pop eax + jmp .end_of_text + @@: + cmp byte[edx], 13 + je .newline_soft + cmp byte[edx], 10 + je .newline_hard + + cmp byte[edx], 3 ; escape code for chat colors + jne .no_colors + inc edx + call dec_to_esi + jz .line + mov ecx, [chat_colors + 4*esi] + or ecx, 0x30000000 + + cmp byte[edx], ',' ; background color? + jne .line + inc edx + call dec_to_esi + jz .line + mov edi, [chat_colors + 4*esi] + or ecx, 0x40000000 + .no_colors: + +; Some non-printable, just skip it + inc edx + jmp .line + +;------------------------------------------- +; Count characters until 0, 10, 13 or 3 byte + + .printable: + push edx + xor esi, esi + dec esi + .next_char: + inc esi + cmp esi, [textbox_width] + je .cnt_done + mov al, byte[edx] + cmp al, 0x20 + jb .cnt_done + + inc edx + test al, 10000000b + jz .next_char ; 1 byte wide + + add edx, 3 + and al, 11111000b + cmp al, 11110000b + je .next_char ; 4 bytes wide + + dec edx + and al, 11110000b + cmp al, 11100000b + je .next_char ; 3 bytes wide + dec edx ; 2 bytes wide + jmp .next_char + .cnt_done: + mov eax, edx + pop edx + push eax + mcall 4 ; draw text + pop edx ; next start ptr + + cmp esi, [textbox_width] + je .line_full + imul esi, FONT_WIDTH shl 16 + add ebx, esi + jmp .line + + .newline_hard: + mov ecx, [colors.work_text] + or ecx, 0x30000000 + .newline_soft: + inc edx + .line_full: + and ebx, 0x0000ffff + add ebx, TEXT_X shl 16 + FONT_HEIGHT + pop eax + dec eax + jnz .drawloop + .end_of_text: + + ret + + + + +dec_to_esi: + + xor esi, esi + .loop: + movzx eax, byte[edx] + sub al, '0' + jb .done + cmp al, 9 + ja .done + inc edx + lea esi, [esi*4 + esi] ; esi * 5 + lea esi, [esi*2 + eax] ; esi * 2 + eax + jmp .loop + .done: + cmp esi, 16 + jae .fail + ret + + .fail: + xor esi, esi + ret + + + +if TIMESTAMP +print_timestamp: + + pusha + mcall 3 ; get system time + mov ebx, eax + + mov al, '[' + call print_char + mov ecx, TIMESTAMP + .loop: + mov al, bl + shr al, 4 + add al, '0' + call print_char + + mov al, bl + and al, 0x0f + add al, '0' + call print_char + + dec ecx + jz .done + + mov al, ':' + call print_char + shr ebx, 8 + jmp .loop + .done: + mov al, ']' + call print_char + mov al, ' ' + call print_char + + popa + ret +end if \ No newline at end of file diff --git a/matrix/gui/matrix/userparser.inc b/matrix/gui/matrix/userparser.inc new file mode 100644 index 0000000..a65ab7c --- /dev/null +++ b/matrix/gui/matrix/userparser.inc @@ -0,0 +1,273 @@ +user_parser: + mov ebp, [window_active] ; print to the current window + mov [window_print], ebp + + mov ecx, [edit1.size] + test ecx, ecx + jz .ret ; nothing to do here + + ; copy input straight to cmd buffer + mov esi, input_text + mov edi, user_command + rep movsb ; copies ecx bytes + mov byte[edi], 0x0 ; null terminate + + mov eax, [edit1.size] + mov [user_command.size], eax + + ; is it a slash command? + cmp byte [user_command], '/' + je .command + + .send_privmsg: + mov al, '<' + call print_char + + mov esi, user_name + call print_asciiz + + mov al, '>' + call print_char + mov al, ' ' + call print_char + + mov esi, user_command + call print_asciiz + + mov al, 10 + call print_char + + .ret: + ret + + .command: + ; slice the command string at the first space + mov esi, user_command + mov [cmd_args], 0 + .find_space: + lodsb + test al, al + jz .cmd_end + cmp al, ' ' + jne .find_space + + ; space found, terminate here + mov byte [esi-1], 0 + mov [cmd_args], esi + .cmd_end: + + ; /help + mov esi, user_command + mov edi, cmd_help + call streq + test eax, eax + jnz .do_help + + ; /login + mov esi, user_command + mov edi, cmd_login + call streq + test eax, eax + jnz .do_login + + ; /rooms + mov esi, user_command + mov edi, cmd_rooms + call streq + test eax, eax + jnz .do_rooms + + ; /peek + mov esi, user_command + mov edi, cmd_peek + call streq + test eax, eax + jnz .do_peek + + ; /logout + mov esi, user_command + mov edi, cmd_logout + call streq + test eax, eax + jnz .do_logout + + ; invalid command + mov esi, str_invalid_cmd + call print_asciiz + jmp .done_cmd + + .do_help: + mov esi, str_help + call print_asciiz + jmp .done_cmd + + .do_login: + ; get username/pass from args + mov esi, [cmd_args] + test esi, esi + jz .login_err + push esi ; username starts at esi + + .find_pw_space: + lodsb + test al, al + jz .login_err_pop + cmp al, ' ' + jne .find_pw_space + + mov byte [esi-1], 0 ; null terminate username + mov edx, esi ; password starts at esi + pop esi + ; we now have ESI = user, EDX = pass + + cmp dword [login_status], 0 + je .start_conn + call matrix_disconnect + + .start_conn: + call init_tls + test eax, eax + jnz .login_tls_err + + call matrix_connect + test eax, eax + jnz .login_conn_err + call matrix_login_api + + jmp .done_cmd + + .login_err_pop: + pop esi + .login_err: + ; invalid usage + mov esi, str_invalid_cmd + call print_asciiz + jmp .done_cmd + .login_tls_err: + mov esi, str_tls_err + call print_asciiz + jmp .done_cmd + .login_conn_err: + mov esi, str_conn_err + call print_asciiz + jmp .done_cmd + + .do_rooms: + cmp dword [login_status], 0 + je .not_logged_in_err + + call matrix_disconnect + call init_tls + test eax, eax + jnz .rooms_conn_err + + call matrix_connect + test eax, eax + jnz .rooms_conn_err + + call matrix_get_rooms_api + + jmp .done_cmd + + .not_logged_in_err: + mov esi, str_not_logged_in + call print_asciiz + jmp .done_cmd + .rooms_conn_err: + mov esi, str_conn_err + call print_asciiz + jmp .done_cmd + + .do_peek: + cmp dword [login_status], 0 + je .not_logged_in_err + + mov esi, [cmd_args] + test esi, esi + jz .peek_usage_err + + call atoi + test eax, eax + jz .peek_usage_err + + cmp eax, [room_count] ; now eax = room index (1-based) + jg .peek_bounds_err + + ; reconnect + push eax + call matrix_disconnect + call init_tls + test eax, eax + jnz .peek_conn_err_pop + + call matrix_connect + test eax, eax + jnz .peek_conn_err_pop + pop eax + + call matrix_peek_api + jmp .done_cmd + + .peek_conn_err_pop: + pop eax + .peek_conn_err: + mov esi, str_conn_err + call print_asciiz + jmp .done_cmd + + .peek_usage_err: + mov esi, str_peek_usage_err + call print_asciiz + jmp .done_cmd + .peek_bounds_err: + mov esi, str_peek_bounds_err + call print_asciiz + jmp .done_cmd + + .do_logout: + cmp dword [login_status], 0 + je .already_logged_out + mov dword [login_status], 0 + mov dword [login_status], 0 + + call matrix_disconnect + call init_tls + test eax, eax + jnz .already_logged_out + + call matrix_connect + test eax, eax + jnz .already_logged_out + + call matrix_logout_api + + call matrix_disconnect + + .already_logged_out: + mov esi, str_logout_msg + call print_asciiz + jmp .done_cmd + + .done_cmd: + ret + +; basic strcmp +; in: esi = str1, edi = str2 +; out: eax = 1 if match else 0 +streq: + push esi edi ecx + .str_loop: + lodsb + mov ah, byte [edi] + inc edi + cmp al, ah + jne .not_equal + test al, al + jnz .str_loop + mov eax, 1 + pop ecx edi esi + ret + .not_equal: + xor eax, eax + pop ecx edi esi + ret diff --git a/matrix/gui/matrix/window.inc b/matrix/gui/matrix/window.inc new file mode 100644 index 0000000..6b669d2 --- /dev/null +++ b/matrix/gui/matrix/window.inc @@ -0,0 +1,227 @@ +; window_create_textbox +; Initialises the data structure for our multiline textbox +; +; in: window ptr in ebx +; out: eax = 0 on error +; ecx, edi = destroyed + +window_create_textbox: + + push ebx +; allocate the window data block + mcall 68, 12, sizeof.window_data + test eax, eax + pop ebx + jz .fail + +; fill it with all zeros + push eax + mov edi, eax + mov ecx, (sizeof.window_data+3)/4 + xor eax, eax + rep stosd + pop eax + + mov [ebx + window.data_ptr], eax + mov [ebx + window.flags], FLAG_SCROLL_LOW + + add eax, window_data.text+2 ; let text begin at offset 2, this way the text will be prepended with two null bytes + mov [ebx + window.text_start], eax + mov [ebx + window.text_print], eax + mov [ebx + window.text_write], eax + mov [ebx + window.text_scanned], eax + mov [ebx + window.text_lines], 0 + mov [ebx + window.text_line_print], 0 + add eax, TEXT_BUFFERSIZE-1 + mov [ebx + window.text_end], eax + + .fail: + ret + + +; window_set_name +; Fills in the window name in window structure +; +; IN: esi = ptr to name +; ebx = window ptr +; OUT: esi = ptr to next parameter +; ebx = window ptr +; eax, ecx, edx, edi = destroyed + +window_set_name: + + lea edi, [ebx + window.name] + mov ecx, MAX_WINDOWNAME_LEN + .loop: + lodsb + cmp al, 0x21 ; name ends with 0, space or ! + jbe .addzero + stosb + dec ecx + jnz .loop + .addzero: + xor al, al + stosb + + push esi ebx + call draw_window_tabs + pop ebx esi + + ret + +window_close_all: + + .loop: + call window_close + + cmp [window_print], 0 + jne .loop + + ret + + +window_close: ; closes the 'print' window + +; Remove the window (overwrite current structure with trailing ones) + mov edi, [window_print] + push [edi + window.data_ptr] ; remember data ptr so we can free it later + lea esi, [edi + sizeof.window] + mov ecx, windows + MAX_WINDOWS*sizeof.window + sub ecx, esi + rep movsb + +; Completely zero the trailing window block (there will always be one!) + mov ecx, sizeof.window + xor al, al + rep stosb + +; Clear the window data block to erase the chat history and nicknames + mov edi, [esp] + mov ecx, sizeof.window_data ; TEXT_BUFFERSIZE + MAX_USERNAME_LEN * MAX_USERS + rep stosb + +; And now free the window data block + pop ecx + mcall 68, 13 + +; We closed this window so we need to show another + ; Is there still a window in the active position? + mov edi, [window_active] + cmp [edi + window.data_ptr], 0 + jne .redraw + ; Did we just close the last window? + cmp edi, windows + je .closed_last + ; Nope, move one window to the left + sub edi, sizeof.window + mov [window_active], edi + mov [window_print], edi + jmp .redraw + + .closed_last: + xor edi, edi + mov [window_active], edi + mov [window_print], edi + +; At last, redraw everything + .redraw: + call draw_window + + ret + + +; window_find: +; search for a window with given name in the window list +; +; IN: esi = ptr to start of window name +; OUT: ebx = window ptr, or 0 if none found +; esi = ptr to end of window name, if window was found + +window_find: + + mov ebx, windows + mov eax, MAX_WINDOWS + .scanloop: + push esi + cmp [ebx + window.type], WINDOWTYPE_NONE + je .try_next + lea edi, [ebx + window.name] + mov ecx, MAX_WINDOWNAME_LEN + repe cmpsb + cmp byte[edi-1], 0 ; last equall character was null? yes, the strings match! + je .got_it + cmp byte[edi], 0 ; we're at the end of string1.. ? + jne .try_next + cmp byte[esi], 0x21 ; and the end of string2? yes! + jbe .got_it + .try_next: + pop esi + add ebx, sizeof.window + dec eax + jnz .scanloop + + xor ebx, ebx + ret + + .got_it: + add esp, 4 + ret + + + +; window_open: +; open a window with a given name, if it does not exist, create it +; This procedure only affects window_print ptr, not window_active! +; +; IN: esi = ptr to ASCIIZ windowname +; OUT: esi = ptr to next parameter +; ebx = window ptr/0 on error + +window_open: + +; Skip heading spaces + lodsb + cmp al, ' ' + je window_open + cmp al, ':' + je window_open + dec esi + + call window_find + test ebx, ebx + jnz .got_it + +; create channel window - search for empty slot + .create_it: + mov ebx, windows + mov ecx, MAX_WINDOWS + .scanloop2: + cmp [ebx + window.type], WINDOWTYPE_NONE + je .free_found + add ebx, sizeof.window + dec ecx + jnz .scanloop2 + jmp .error + + .free_found: + call window_create_textbox + test eax, eax + jz .error + mov [ebx + window.type], WINDOWTYPE_CHAT ; FIXME: let caller handle this ? + + call window_set_name + + .got_it: + lodsb + cmp al, ' ' + je .got_it + cmp al, ':' + je .got_it + dec esi + + mov [window_print], ebx + ret + + .error: + xor ebx, ebx + ret \ No newline at end of file