;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2010-2013. All rights reserved.    ;;
;; Distributed under terms of the GNU General Public License       ;;
;;                                                                 ;;
;;  Synergyc.asm - Synergy client for KolibriOS                    ;;
;;                                                                 ;;
;;  Written by hidnplayr@kolibrios.org                             ;;
;;                                                                 ;;
;;          GNU GENERAL PUBLIC LICENSE                             ;;
;;             Version 2, June 1991                                ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

format binary as ""

BUFFERSIZE      = 1024
DEFAULTPORT     = 24800

use32
        org     0x0

        db      'MENUET01'      ; signature
        dd      1               ; header version
        dd      start           ; entry point
        dd      i_end           ; initialized size
        dd      mem+0x1000      ; required memory
        dd      mem+0x1000      ; stack pointer
        dd      0               ; parameters
        dd      path            ; path

include '../../macros.inc'
purge mov,add,sub
include '../../proc32.inc'
include '../../dll.inc'

include '../../network.inc'

start:

        mov     edi, path       ; Calculate the length of zero-terminated string
        xor     al, al
        mov     ecx, 1024
        repne   scasb
        dec     edi
        mov     esi, filename   ; append the path with '.ini'
        movsd
        movsb

        mcall   68, 11
        mcall   66, 1, 1

        stdcall dll.Load, @IMPORT
        test    eax, eax
        jnz     exit

        push    1
        call    [con_start]

        push    title
        push    25
        push    80
        push    25
        push    80
        call    [con_init]

        push    str0
        call    [con_write_asciiz]

restart:

        invoke  ini.get_str, path, str_remote, str_ip, buffer_ptr, 16, 0
        test    eax, eax
        jnz     error

        invoke  ini.get_int, path, str_remote, str_port, DEFAULTPORT
        xchg    al, ah
        mov     [sockaddr1.port], ax

        push    str1
        call    [con_write_asciiz]

        push    buffer_ptr
        call    [con_write_asciiz]

        push    newline
        call    [con_write_asciiz]

        mcall   socket, AF_INET4, SOCK_STREAM, 0
        cmp     eax, -1
        je      error
        mov     [socketnum], eax

; resolve name
        push    esp             ; reserve stack place
        push    esp             ; ptr to result
        push    0               ; addrinfo hints
        push    0               ; servname
        push    buffer_ptr      ; hostname
        call    [getaddrinfo]
        pop     esi
; test for error
        test    eax, eax
        jnz     error

        mov     eax, [esi+addrinfo.ai_addr]
        mov     eax, [eax+sockaddr_in.sin_addr]
        mov     [sockaddr1.ip], eax

        mcall   connect, [socketnum], sockaddr1, 18
        test    eax, eax
        jnz     error

        push    str7
        call    [con_write_asciiz]

        mcall   40, EVM_STACK

login:
        call    wait_for_data

        push    buffer_ptr + 4
        call    [con_write_asciiz]

        cmp     dword [buffer_ptr], 11 shl 24   ; length of string
        jne     error
        cmp     dword [buffer_ptr + 4], 'Syne'
        jne     error
        cmp     word [buffer_ptr + 8], 'rg'
        jne     error
        cmp     byte [buffer_ptr + 10], 'y'
        jne     error

        push    str2
        call    [con_write_asciiz]

        lea     edi, [buffer_ptr + 11 + 4 + 4]
        invoke  ini.get_str, path, str_local, str_name, edi, 255, 0
        xor     al , al
        mov     ecx, 256
        repne   scasb
        sub     edi, buffer_ptr + 1 + 4
        mov     esi, edi
        bswap   edi
        mov     dword [buffer_ptr], edi
        mov     edi, esi
        sub     edi, 11 + 4
        bswap   edi
        mov     dword [buffer_ptr + 11 + 4], edi
        add     esi, 4

        mcall   send, [socketnum], buffer_ptr, , 0

mainloop:
        call    wait_for_data
        mov     edi, buffer_ptr

  .command:
        push    eax edi

        cmp     dword [edi + 4], 'QINF' ; query screen info
        je      .qinf

        cmp     dword [edi + 4], 'CALV' ; alive ?
        je      .calv

        cmp     dword [edi + 4], 'CINN' ; mouse moved into screen
        je      .cinn

        cmp     dword [edi + 4], 'DCLP' ; Clipboard event
        je      .dclp

        cmp     dword [edi + 4], 'DMMV' ; Mouse moved
        je      .dmmv

        cmp     dword [edi + 4], 'COUT' ; leave screen
        je      .cout

        cmp     dword [edi + 4], 'DMDN' ; mouse button down
        je      .dmdn

        cmp     dword [edi + 4], 'DMUP' ; mouse button released
        je      .dmup

        cmp     dword [edi + 4], 'CNOP' ; no operation
        je      .next

        cmp     dword [edi + 4], 'CIAK' ; resolution changed?
        je      .ciak

;        cmp     dword [edi + 4], 'DMWM' ; mouse wheel moved
;        je      .dmwm

        cmp     dword [edi + 4], 'DKDN' ; Key pressed
        je      .dkdn

        cmp     dword [edi + 4], 'DKUP' ; key up
        je      .dkup

        cmp     dword [edi + 4], 'DKRP' ; key auto repeat
        je      .dkrp

        push    str3
        call    [con_write_asciiz]

        mov     byte[edi+8],0
        add     edi, 4
        push    edi
        call    [con_write_asciiz]

        push    newline
        call    [con_write_asciiz]

  .next:
        pop     edi eax

        mov     ecx, dword [edi]
        bswap   ecx
        add     ecx, 4
        sub     eax, ecx
        jle     mainloop
        add     edi, ecx
        jmp     .command


  .qinf:
        mcall   14      ; get screen info
        add     eax, 0x00010001
        bswap   eax
        mov     dword [screeninfo.size], eax
        mcall   send, [socketnum], screeninfo, screeninfo.length, 0     ; send client name
        jmp     .next


  .calv:
        mcall   send, [socketnum], calv, calv.length, 0                 ; keepalive

        jmp     .next


  .cinn:
        mov     edx, [edi + 8]
        bswap   edx
        mcall   18, 19, 4
        ; ignore sequence number and modify key mask for now
        push    str6
        call    [con_write_asciiz]
        jmp     .next

  .dclp:

        jmp     .next

  .dmmv:
        mov     edx, [edi + 8]
        bswap   edx
        mcall   18, 19, 4
        mcall   send, [socketnum], cnop, cnop.length, 0     ; reply with NOP
        jmp     .next

  .cout:
        ; TODO: hide cursor
        push    str8
        call    [con_write_asciiz]
        jmp     .next

  .dmdn:
        movzx   eax, byte[edi + 8]
        cmp     eax, 5
        ja      .next
        movzx   eax, [mousecodes + eax]
        or      [mousestate], eax
        mcall   18, 19, 5, [mousestate]
        mcall   send, [socketnum], cnop, cnop.length, 0     ; reply with NOP
        jmp     .next

  .dmup:
        movzx   eax, byte[edi + 8]
        cmp     eax, 5
        ja      .next
        movzx   eax, [mousecodes + eax]
        not     eax
        and     [mousestate], eax
        mcall   18, 19, 5, [mousestate]
        mcall   send, [socketnum], cnop, cnop.length, 0     ; reply with NOP
        jmp     .next

  .ciak:
        jmp     .next

;  .dmwm:
;        jmp     .next

  .dkdn:
        movzx   edx, word[edi + 8 + 4]  ; key button
        rol     dx, 8
        mcall   72, 1, 2                ; send key
        jmp     .next

  .dkrp:
        movzx   edx, word[edi + 8 + 6]
        rol     dx, 8
        mcall   72, 1, 2                ; send key
        jmp     .next

  .dkup:
        movzx   edx, word[edi + 8 + 4]
        rol     dx, 8
        add     edx, 128
        mcall   72, 1, 2                ; send key

        jmp     .next

error:
        push    str_err
        call    [con_write_asciiz]

wait_for_key:
        call    [con_getch2]

        mcall   close, [socketnum]

        push    1
        call    [con_exit]
exit:

        mcall   -1


wait_for_data:
        mcall   recv, [socketnum], buffer_ptr, BUFFERSIZE, 0
        inc     eax
        jz      error
        dec     eax
        jz      closed

        cmp     eax, 8
        jb      wait_for_data   ; FIXME

        ret

closed:

        push    str_cls
        call    [con_write_asciiz]

        mcall   close, [socketnum]

        mcall   5, 200

        jmp     restart



; data
title   db      'Synergy client',0
str0    db      'Welcome to the software KM switch for KolibriOS.',10,10,0
str1    db      'Connecting to: ',0
str7    db      'Connected!',10,0
str2    db      10,'Handshake received',10,0
str3    db      'Unsupported command: ',0
newline db      10,0
str6    db      'Entering screen',10,0
str8    db      'Leaving screen',10,0
str_err db      'Uh-Oh.. some error occured !',10,'Press any key to quit.',0
str_cls db      'Server disconnected',10,0

screeninfo:
        dd (screeninfo.length - 4) shl 24
        db 'DINF'
        dw 0    ; coordinate of leftmost pixel
        dw 0    ; coordiante of topmost pixel
  .size:
        dw 0    ; width
        dw 0    ; height

        dw 0    ; size of warp zone

        dw 0xb88b        ; x position of the mouse on the secondary screen  (no idea what it means)
        dw 0xbcfb        ; y position of the mouse on the secondary screen
  .length = $ - screeninfo

calv:
        dd (4) shl 24
        db 'CALV'
  .length = $ - calv + 8 ; also send cnop

cnop:
        dd (4) shl 24
        db 'CNOP'
  .length = $ - cnop

mousecodes      db 0, 1b, 100b, 10b, 1000b, 10000b
mousestate      dd 0


sockaddr1:
        dw AF_INET4
.port   dw 0
.ip     dd 0
        rb 10

filename        db      '.ini', 0
str_local       db      'local', 0
str_name        db      'name', 0
str_remote      db      'remote', 0
str_port        db      'port', 0
str_ip          db      'ip', 0

; import
align 16
@IMPORT:

library console,        'console.obj',\
        network,        'network.obj',\
        libini,         'libini.obj'

import  network,\
        getaddrinfo,    'getaddrinfo'

import  console,                \
        con_start,              'START',\
        con_init,               'con_init',\
        con_write_asciiz,       'con_write_asciiz',\
        con_exit,               'con_exit',\
        con_gets,               'con_gets',\
        con_cls,                'con_cls',\
        con_getch2,             'con_getch2',\
        con_set_cursor_pos,     'con_set_cursor_pos'

import  libini,\
        ini.get_str,    'ini_get_str',\
        ini.get_int,    'ini_get_int'

align   4
i_end:
socketnum       dd ?
buffer_ptr      rb BUFFERSIZE
path            rb 4096
mem: