diff --git a/programs/system/clip/trunk/@clip.ASM b/programs/system/clip/trunk/@clip.ASM new file mode 100644 index 0000000000..916374bede --- /dev/null +++ b/programs/system/clip/trunk/@clip.ASM @@ -0,0 +1,413 @@ +; <--- description ---> +; compiler: FASM +; name: Clipboard server/daemon +; version: 0.2 +; author: barsuk + +; <--- include all MeOS stuff ---> +include "lang.inc" +include "..\..\..\MACROS.INC" + +; <--- start of MenuetOS application ---> +MEOS_APP_START + +;define DEBUG TRUE +define DEBUG FALSE +include "bdebug.inc" + +DEFAULT_SIZE = 0x10010 ; 64K + size of headers +MAX_SIZE = 0x01000000 ; 16 M +MAX_FORMAT = 16 +DELAY = 10 ; 1/10 sec between sending attempts +ATTEMPT = 5 ; number of sending attempts + + +; <--- start of code ---> +CODE + + ; во-первых, надо убить все @clip, какие найдутся: + ; демон буфера обмена должен быть только один! + ; и им окажется самый молодой из демонов... + + mov ecx, 2 ; первый номер слота для проверки + mov ebp, info + mov ebx, ebp + mov edx, 18 +.next_process: + mov eax, 9 + int 0x40 + cmp dword [ebx + 10], '@CLI' + jnz .differ + cmp byte [ebx + 14], 'P' + jnz .differ +.similar: + xchg eax, edx + mov ebx, 2 + int 0x40 + mov ebx, ebp + xchg eax, edx +.differ: + inc ecx + cmp ecx, eax + jae .done ; process not found + jmp .next_process + +.done: + mov eax, 68 ; init heap + mov ebx, 11 + int 0x40 + + mov eax, 66 ; set hotkey: Ctrl-Alt-V + mov ebx, 4 + mov ecx, 47 + mov edx, 0x110 ; 0x110 + int 0x40 +print "set hot key (1 bad)" +pregs + + mov ebp, DEFAULT_SIZE + + mov eax, 68 ; get memory for buffer + mov ebx, 12 + mov ecx, ebp + int 0x40 + mov esi, IPC_buffer + mov [esi], eax + + mov eax, 60 ; define buffer for IPC + mov ebx, 1 + mov ecx, [esi] + mov edx, ebp + int 0x40 + + mov eax, 40 + mov ebx, 01000010b ; + int 0x40 + +wait_event: ; main cycle + mov eax, [IPC_buffer] + mov dword [eax], 0 ; unlock buffer + mov dword [eax + 4], 8 + + mov eax, 10 ; wait + int 0x40 +print "event" +pregs + + ;dec eax + ;dec eax + cmp eax, 2 + jnz ipc_event + +print "hotkey" + + mov eax, 2 + int 0x40 +pregs + cmp ah, 47 ; "v" up + jnz wait_event + +print "hotkey v up" + ; it is hotkey + mov eax, 1 + mov ebx, eax + call find_format +pregs + cmp eax, -1 + jz wait_event + mov esi, [data_buffer + eax * 4] + mov edi, [data_size + eax * 4] + mov ecx, 2 +print "ping" +.next: + mov eax, 72 + movzx edx, byte [esi] + int 0x40 ; here we should pause if eax = 1 +pregs + + inc esi + dec edi + jnz .next + jmp wait_event + +ipc_event: + +; we get an IPC message + +print "recv. " + + mov eax, [IPC_buffer] + mov dword [eax], 1 ; lock buffer + mov dword [eax + 4], 8 + +dph1 dword [eax] ; lock +dph1 dword [eax + 4] ; current used size +dph1 dword [eax + 8] ; pid +dph1 dword [eax + 12] ; len +print "" +dph1 dword [eax + 16] ; cmd << 16 | fmt +dph1 dword [eax + 20] +dph1 dword [eax + 24] + + mov ebx, [eax + 8 + 4] + cmp ebx, 8 + jb wait_event ; all my messages have 8-byte header + ; so ignore this one + + movzx ecx, word [eax + 8 + 8] ; command + cmp ecx, 1 + jz command_setsize + cmp ecx, 2 + jz command_set + cmp ecx, 3 + jz command_getsize + cmp ecx, 4 + jz command_get + cmp ecx, 5 + jz command_delete + jmp wait_event ; unrecognised command + +command_setsize: + +; the only thing we really need to do here is to grow IPC buffer +; no changes are done to real buffer of chosen format + +; the bad thing is that IPC buffer grows and never becomes less. +; i want to fix this in the next version. + +print "set size" + + mov esi, [IPC_buffer_size] + mov ecx, [eax + 24] ; size + add ecx, 0x18 ; for headers +;pregs + cmp esi, ecx + jae wait_event +print "ipc buffer resize from esi to ecx" +pregs + + mov ebp, MAX_SIZE ; sort of protection. forbid transfer of more + cmp ecx, ebp ; than 16,7 Mb of data through buffer + jbe not_much + mov ecx, ebp + +not_much: + xchg eax, edi ; edx := [IPC_buffer] + add ecx, 0x18 ; for headers + + mov [IPC_buffer_size], ecx + mov eax, 68 + mov ebx, 12 + int 0x40 ; get memory +print "get mem for new buf, eax" +pregs + mov [IPC_buffer], eax + + mov dword [eax + 4], 8 + mov edx, ecx + mov ecx, eax + mov eax, 60 + mov ebx, 1 + int 0x40 ; make it IPC buffer + + mov ecx, edi + mov eax, 68 + mov ebx, 13 + int 0x40 ; free old IPC buffer + + jmp wait_event + +command_set: +print "set" + +; here we put the data transfered to us to one of internal buffers + +;;;outs eax + 0x18 + + movzx eax, word [eax + 18] ; format id + call find_format + cmp eax, -1 + jz new_format + mov edx, eax + + ; free old buffer of this format + + mov ecx, [data_buffer + edx * 4] + mov eax, 68 + mov ebx, 13 + int 0x40 + jmp set_buffer + +new_format: + mov eax, data_count ; allocate a buffer + mov edx, [eax] + inc dword [eax] ; no protection, no checks. very bad :( +set_buffer: + mov eax, [IPC_buffer] + mov ecx, dword [eax + 12] + sub ecx, 8 ; get length of data itself + ; subtract size of my headers +;pregs + mov [data_size + edx * 4], ecx ; save length of data + + mov eax, 68 + mov ebx, 12 + int 0x40 + mov [data_buffer + edx * 4], eax + +; copy data from IPC to new buffer + mov esi, [IPC_buffer] + mov ebp, esi + add esi, 24 ; start of data + mov edi, eax + rep movsb ; oh, me knows that it's bad + + mov eax, ebp + movzx ecx, word [eax + 18] ; format id + mov [data_format + edx * 4], ecx + + ; debug +; print "set debug" +; mov eax, [data_buffer + edx * 4] +; mov ebx, [data_size + edx * 4] +; mov ecx, [data_format + edx * 4] +; pregs + + jmp wait_event + +command_delete: + movzx eax, word [eax + 18] ; format id + cmp eax, -1 + jz command_clear + call find_format + cmp eax, -1 + jz wait_event + mov ecx, [data_buffer + eax * 4] + mov [data_format + eax * 4], 0 + mov eax, 68 + mov ebx, 13 + int 0x40 + jmp wait_event + +command_clear: + mov eax, 68 + mov ebx, 13 + mov edx, data_buffer +.next: + mov ecx, [edx] + jecxz .nofree + int 0x40 +.nofree: + mov [edx + data_size - data_buffer], 0 + add edx, 4 + cmp edx, data_format + jnae .next + jmp wait_event + +command_getsize: + +print "get size" + + ; we should send reply, containing size of data with chosen format id + movzx eax, word [eax + 18] ; format id + mov esi, 4 + call find_format +;pregs + cmp eax, -1 + jz zero_size + + lea edx, [data_size + eax * 4] + mov eax, [edx] + jmp send_getsize +zero_size: + mov edx, dw_zero ; send 0 to indicate that the buffer is empty +send_getsize: + jmp send_msg + +command_get: +print "get" + +; we need to send the data + + ; [:||||:] + + movzx eax, word [eax + 18] ; format id + call find_format + cmp eax, -1 + jz wait_event + mov edi, eax ; а нефиг + + mov edx, [data_buffer + edi * 4] + mov esi, [data_size + edi * 4] + ;jmp send_msg + +send_msg: + + ; for debugging +; mov eax, 5 +; mov ebx, DELAY*5 +; int 0x40 + + mov ebp, ATTEMPT ; number of attempts to send + mov eax, [IPC_buffer] + mov ecx, [eax + 8] ; sender PID + mov eax, 60 + mov ebx, 2 + int 0x40 +print "send data result eax" +pregs + cmp eax, 2 + jz .wait + cmp eax, 3 ; it is strange.. + jz .wait ; maybe he needs time to resize his buf + jmp wait_event +.wait: + dec ebp + jz wait_event + mov eax, 5 ; sleep a bit + mov ebx, DELAY + int 0x40 + jmp send_msg + + +find_format: ; small function returning number of format + +;print "find format" + push ebx + mov ebx, eax ; format id + and ebx, 0xffff ; it is word + xor eax, eax +next: + cmp [data_format + eax * 4], ebx + jz found + inc eax + cmp eax, MAX_FORMAT + jb next +not_found: + or eax, -1 +found: +;pregs + pop ebx + ret + +; <--- initialised data ---> +DATA + IPC_buffer dd 0 + IPC_buffer_size dd DEFAULT_SIZE ; initially 64K + + info db 1024 dup(0) ; for process info + + data_buffer dd MAX_FORMAT dup(0) ; buffer for some format + ; (256 formats max) + data_format dd MAX_FORMAT dup(0) ; format id + data_size dd MAX_FORMAT dup(0) ; size of buffer + data_count dd 0 ; number of formats used + dw_zero dd 0 ; used to tell that + ; we don't have a format + +; <--- uninitialised data ---> +UDATA + + +MEOS_APP_END +; <--- end of MenuetOS application ---> \ No newline at end of file diff --git a/programs/system/clip/trunk/bdebug.inc b/programs/system/clip/trunk/bdebug.inc new file mode 100644 index 0000000000..86a417ca2c --- /dev/null +++ b/programs/system/clip/trunk/bdebug.inc @@ -0,0 +1,47 @@ +; debugging include file by barsuk +; if DEBUG=TRUE, adds debug.inc +; if DEBUG=FALSE, adds stubs for several macros from debug.inc + +match =TRUE, DEBUG +{ + +include '..\..\..\debug.inc' + +macro dph1 str +\{ + debug_print_hex str + push eax + mov al, ' ' + call debug_outchar + pop eax +\} + +macro outs str +\{ + pushad + lea edx, [str] + call debug_outstr + popad +\} +} + +match =FALSE, DEBUG +{ + +macro print message +\{ +\} + +macro pregs +\{ +\} + +macro dph1 str +\{ +\} + +macro outs str +\{ +\} + +} \ No newline at end of file diff --git a/programs/system/clip/trunk/build_ru.bat b/programs/system/clip/trunk/build_ru.bat new file mode 100644 index 0000000000..3cac8083af --- /dev/null +++ b/programs/system/clip/trunk/build_ru.bat @@ -0,0 +1,7 @@ +@echo lang fix ru >lang.inc +@fasm @clip.asm @clip +@fasm cliptest.asm cliptest +@fasm test2.asm test2 +@erase lang.inc +@kpack @clip +@rem pause \ No newline at end of file diff --git a/programs/system/clip/trunk/clip.inc b/programs/system/clip/trunk/clip.inc new file mode 100644 index 0000000000..b698483573 --- /dev/null +++ b/programs/system/clip/trunk/clip.inc @@ -0,0 +1,359 @@ + +; clipboard client module by barsuk. +; version 0.2 +; see readme.txt + +macro CLIP_BUFFER buf_name, buf_size +{ +#buf_name: +.size dd buf_size +.sys1 dd ? +.sys2 dd ? +.data db buf_size dup(?) +} + +_ipc_send: +; internally used routine +; esi->buffer, edx = byte count + + push ebx + push ecx + push ebp + + mov ebp, ATTEMPT ; number of attempts to send + xchg edx, esi +.send_again: + mov eax, 5 + mov ebx, SEND_DELAY + int 0x40 + + mov eax, 60 + mov ebx, 2 + mov ecx, [clipboard_init.clipserv_pid] + int 0x40 +print "send result eax" +pregs + + or eax, eax + jz .ok + cmp eax, 2 + jz .wait + jmp .err +.wait: + dec ebp + jz .err + jmp .send_again +.ok: + mov eax, 1 + jmp .exit +.err: + xor eax, eax +.exit: + pop ebp + pop ecx + pop ebx + xchg edx, esi + ret + +_ipc_recv: +; used internally. +; edx = default event mask of thread(!!!) +; esi -> CLIP_buffer +; result: eax == 1 - suc, 0 - err + + push ebx + push ecx + + mov dword [esi + 4], 0 ; unlock buffer + mov dword [esi + 8], 0 ; + + push edx + mov eax, 60 ; register buffer + mov ebx, 1 + lea ecx, [esi + 4] + mov edx, [esi] + add edx, 8 +;print "register buffer" +;pregs + int 0x40 + pop edx + +.wait: + mov eax, 40 ; listen for IPC + mov ebx, 01000000b + int 0x40 + + mov eax, 23 ; wait for IPC event + mov ebx, RECV_DELAY ; not too long + int 0x40 + cmp eax, 7 + jnz .err + + mov eax, [clipboard_init.clipserv_pid] ; not message from + cmp eax, [esi + 4] ; daemon, so ignore + jnz .wait + + mov dword [esi + 4], 1 ; lock so it will not be spoiled + +print "cli recv" +dph1 [esi] +dph1 [esi+4] +dph1 [esi+8] +dph1 [esi+12] +dph1 [esi+16] +dph1 [esi+20] +print "" + + mov eax, 40 ; restore event mask + mov ebx, edx + int 0x40 + mov eax, 1 + jmp .exit +.err: + xor eax, eax +.exit: + pop ecx + pop ebx + ret + + +clipboard_init: +; action: define IPC buffer and find clipserv process +; output: eax == 0 - error, eax == 1 - success + + push ebx + push ecx + push edx + +; mov eax, 60 ; define buffer for IPC +; mov ebx, 1 +; mov ecx, .IPC_real_buffer +; mov edx, IPC_buffer_size +; int 0x40 + +;print "buffer defined" + +; mov [.IPC_buffer], ecx + + push ebp + mov ebp, 1 +.next_process: + mov eax, 9 + mov ebx, .process_info + mov ecx, ebp + int 0x40 + mov ecx, eax + mov ebx, .process_info + 10 + mov eax, [ebx] + cmp eax, '@CLI' + jnz .differ + mov al, [ebx + 4] + cmp al, 'P' + jnz .differ + jmp .similar + +; mov edx, .clipserv_name +;.compare: +; mov al, [edx] +; cmp al, 0 +; jz .similar +; cmp al, [ebx] +; jnz .differ +; inc edx +; inc ebx +; jmp .compare +.differ: + inc ebp + cmp ebp, ecx + jae .err ; process not found + jmp .next_process +.similar: +;print "found server" + mov eax, dword [.process_info + 30] + mov [.clipserv_pid], eax + + mov eax, 1 + jmp .exit +.err: +;print "no server" + xor eax, eax +.exit: + pop ebp + pop edx + pop ecx + pop ebx + ret +.clipserv_pid dd 0 +.process_info db 1024 dup(0) +;;.clipserv_name db '@clip',0 +;.IPC_buffer dd .IPC_real_buffer ; sorry +;.IPC_real_buffer db IPC_buffer_size dup(0) + +clipboard_write: +; action: put to clipboard data in format (ax) from buffer +; input: esi -> CLIP_buffer, ax = format id +; output: eax == 0 - error, eax == 1 - success + + push ecx + push edx + push edi + + mov edx, [esi] ; CLIP_buffer.size + + push edx + push esi + mov [.msg_set_size + 2], ax + mov word [esi + 4 + 2], ax ; format id to low word of sys1 + mov dword [.msg_set_size + 8], edx + mov esi, .msg_set_size + mov edx, 12 + call _ipc_send + pop esi + pop edx + or eax, eax + jz .err + + mov word [esi + 4], 2 ; COMMAND_SET to high word of sys1 + add esi, 4 ; esi->buffer.sys1 + add edx, 8 + call _ipc_send + sub esi, 4 + or eax, eax + jz .err + + mov eax, 1 +print "write success" + jmp .exit +.err: +print "write failed" + xor eax, eax +.exit: + pop edi + pop edx + pop ecx + ret + +.msg_set_size dw 1 + dw 1 + dd 0 + dd 0 + +clipboard_read: +; esi -> CLIP_buffer, ax = format id +; edx - маска событий по умолчанию +; result: eax = 1 - success, 0 - general failure, +; -1 - buffer too small +; edx = size of data + + push ebx + push ecx + push ebp + push esi + push edi + + mov ebp, edx + + ; get size + + mov edi, esi + mov esi, .msg_get_size + mov word [esi], 3 + mov [esi + 2], ax + mov [.msg_get_buf + 2], ax + mov edx, 8 + call _ipc_send + or eax, eax + jz .err + + ;;mov edx, DEFAULT_MASK + mov edx, ebp + mov esi, .buf_for_size + mov dword [esi], 4 + call _ipc_recv + or eax, eax + jz .err + + mov eax, [esi + 12] ; space we need + mov edx, [edi] ; space we have +print "read size eax" +pregs + mov ecx, eax + cmp eax, edx + ja .size + or eax, eax + jz .err + mov ebx, eax + + mov esi, .msg_get_buf + mov word [esi], 4 + mov edx, 8 + call _ipc_send + or eax, eax + jz .err + +;print "send fuck" + + mov edx, ebp + mov esi, edi + call _ipc_recv + or eax, eax + jz .err + +print "read get data" + + mov edx, ebx + mov eax, 1 +print "read ok" + jmp .exit ; i'm an idiot. Never will I code at night again + ; i put jz instead of jmp. + +.size: +print "buffer small" + mov edx, eax + or eax, -1 + jmp .exit +.err: +print "read error" + xor eax, eax +.exit: + pop edi + pop esi + pop ebp + pop ecx + pop ebx + ret + +.msg_get_size dw 0 + dw 1 + dd 0 +.msg_get_buf dw 0 + dw 1 + dd 0 +.buf_for_size dd 0 + dd 0 + dd 0 + dd 0 + + +clipboard_delete: +; ax = format id to delete +; result: eax = 0 error, = 1 ok + + push edx + push esi + + mov esi, .msg_del + mov word [esi], 5 + mov [esi + 2], ax + mov edx, 8 + + call _ipc_send + + pop esi + pop edx + + ret +.msg_del dw 0 + dw 1 + dd 0 + diff --git a/programs/system/clip/trunk/clip.txt b/programs/system/clip/trunk/clip.txt new file mode 100644 index 0000000000..08872a3cb6 --- /dev/null +++ b/programs/system/clip/trunk/clip.txt @@ -0,0 +1,168 @@ +Общесистемный буфер обмена для ОС Колибри. +Информация для разработчиков. + +Кратко: Буфер реализован через процесс-демон и IPC-сообщения. +Для проверки нужно запустить @clip(демон) и +(cliptest и доску отладки) или test2(можно несколько). + +1. Демон @clip и его команды. + +Демон @clip реализован как процесс, не создающий окон (зачем они ему), +а только слушающий IPC-сообщения. Демон поддерживает до 16 (MAX_FORMAT) буферов для +различных форматов данных, и до 16,7 Мб (MAX_SIZE) данных в каждом буфере +(память отводится динамически). +Id формата данных - число от 0 до 65534 (значение 65535 зарезервировано). + +При запуске демон завершает все другие процессы @clip. + +Команды, передаваемые демону, имеют формат: + +[ Cmd: word | Format: word | Reserved: Dword | Data: ...] + +где Cmd - код команды, +Format - id формата данных, +Reserved - что угодно (не используется), +и Data - данные, смысл которых зависит от команды. + +Демон воспринимает следующие команды: + +Код 1. Set Size. Указать необходимый размер буфера для приема данных. По этой +команде демон при необходимости расширяет свой буфер для IPC-сообщений +(способа уменьшить буфер в текущей реализации нет). +Параметр Data: 1 Dword, содержащий размер данных для передачи. +Длина команды: 12 байт. + +Код 2. Set. Передача данных. По этой команде данные копируются в память демона. +Параметр Data: данные, которые нужно скопировать. +Длина команды: 8 + (длина данных) байт. + +Код 3. Get Size. Получить размер данных, хранящихся в буфере с указанным id +формата. По этой команде демон отправляет ответное IPC-сообщение длиной 4 +байта, содержащее размер данных в буфере. Если данные в буфере отсутствуют, +в сообщении указывается размер 0. +Длина команды: 8 байт. + +Код 4. Get. Получить данные из буфера с указанным id формата. По этой команде +демон отправляет ответное IPC-сообщение нужной длины с данными из буфера. +Если данные в буфере отсутствуют, ответное сообщение не отправляется. +Длина команды: 8 байт. + +Код 5. Delete. Очистить буфер с указанным id формата. Если указать +id формата = 0xFFFF, будут очищены все буферы. +Длина команды: 8 байт. + +Исходный файл - @clip.asm. Если раскомментировать строчку +;define DEBUG TRUE +и закомментировать следующую, то демон начнет писать много буковок на доску +отладки, и по ним можно будет что-то понять о возникающих ошибках. +DEFAULT_SIZE - начальный размер IPC-буфера +MAX_SIZE - ограничение на буфер +MAX_FORMAT - число различных форматов, которые можно засунуть в демона +одновременно (а если больше, сглючит. Тоже надо пофиксить). +DELAY - задержка между попытками отправки сообщения, /100 сек. +ATTEMPT - количество попыток отправки сообщения занятому или не готовому +процессу. + + +2. clip.inc - набор функций для более высокоуровнего общения с демоном @clip. +Реализовано чтение буфера и запись в буфер. + +Пример использования - cliptest.asm (вывод на доску отладки) и +test2.asm. + +При использовании clip.inc необходимо указать следующие значения (числа, +естественно, могут быть другие): +DEFAULT_MASK = 7 ; Маска событий (см. функцию 40) по умолчанию для + ; текущего потока. Нужна, чтобы после приема + ; IPC-сообщения (когда маска меняется на 01000000b) + ; восстановить старую маску (а получить маску ХЗ как). + +SEND_DELAY = 10 ; пауза между попытками при отправке сообщения демону + +RECV_DELAY = 100 ; время ожидания ответа от демона (если за это время + ; не пришел ответ, то ошибка) + ; время - в сотых секунды (как для функции 5). + +ATTEMPT = 5 ; количество попыток отправки сообщения, если демон + ; занят + +После включения clip.inc становятся доступными функции: + +clipboard_init() - поиск процесса @clip. Эту функцию +можно вызывать многократно (например, если демон не нашелся или был +перезапущен), но 1 раз вызвать обязательно нужно. +Возвращает 1 при успехе и 0 при неудаче (не найден демон). + +clipboard_write(esi указывает на буфер в формате CLIP_BUFFER (см. далее), +ax (слово) - id формата ) - запись данных в буфер обмена. +Выполняет команды 1 и 2. Возвращает 1 при успехе и 0 при неудаче +(причины бывают разные: демон не был найден или занят и т.д.). + +clipboard_read(esi указывает на буфер в формате CLIP_BUFFER (см. далее), +ax (слово) - id формата ) - чтение данных из буфера обмена. Выполняет +команды 3 и 4. Возвращает в eax 1 при успехе, -1 при нехватке места в +буфере-приемнике(который в этом случае не меняется) и 0 при прочих ошибках. +В edx(при eax=1 или -1) возвращается действительный размер данных в буфере. + +Замечание. Если приложение использует входящие IPC не только для работы с +буфером обмена, следует обрабатывать сообщения демона @clip вручную, т.к. +иначе может возникнуть такая ситуация: сообщение от другого приложения +попадет в буфер этого приложения для сообщений от демона буфера, +и будет проигнорировано. + +Также можно использовать(после вызова clipboard_init) следующие функции более +низкого уровня: +_ipc_send (esi указывает на обычный буфер, edx - количество байт). +Отправляет IPC-сообщение демону. Отличие от функции 60/2 в том, что _ipc_send +повторяет несколько (точнее, ATTEMPTS) раз попытку отправки, если буфер демона +занят (код 2) или переполнен (код 3), с паузой в SEND_DELAY/100 секунд. +Возвращает 1 при успехе, 0 при ошибке. + +_ipc_recv(esi указывает на буфер в формате CLIP_BUFFER (см. далее), +edx = маска событий потока по умолчанию). +Ждет сообщения от демона в течение RECV_DELAY/100 секунд. При успехе +результат сохраняется в esi. +Возвращает 1 при успехе, 0 при ошибке. + +Формат буфера для работы с буфером обмена: +CLIP_BUFFER +(+0) .size dd ? ; здесь должен быть записан размер + ; собственно буфера (N) + ; при записи данных в буфер, если нужно + ; отправить меньшее количество байт, + ; временно запишите это количество сюда + ; (см. пример test2) + +(+4) .sys1 dd ? ; \ эти поля используются модулем clip.inc + ; - для внутренних целей и не должны +(+8) .sys2 dd ? ; / модифицироваться приложением + +(+12) .data db N dup(?); собственно данные буфера + +Удачи в программировании и отладке! + +; barsuk, 21.08.2008 + + + + +@CLIP - дополнение. Версия 0.2. + +Реализована возможность вставки текста в приложения, не поддерживающие работу +с @clip, при помощи функции 72.1. Однако, из-за особенностей реализации, +приложениям приходит код из edx, независимо от режима ввода(ascii/scancode). +Я решил, что большинство приложений, все же, юзают режим ascii, и поэтому +выбрал его и для @clip (а те приложения, которые используют режим скан-кодов, +получат неверный ввод). +Было бы неплохо внести изменения в ядро (хотя бы возможность узнать чужой +режим ввода). + +Вставка осуществляется нажатием горячей клавиши ctrl-alt-v. Сначала нужно +запустить приложение, работающее с @clip (например, test2), и скопировать в +буфер с id = 1 (обычный текст) какой-либо текст. + +Еще мысль: добавить в eolite (а хорошо бы и kfar) копирование в буфер +текущего пути и имени файла под курсором. Компиляция в fasm и сжатие в +kpack станет удобнее. + +; 08.09.2008 \ No newline at end of file diff --git a/programs/system/clip/trunk/clip_en.txt b/programs/system/clip/trunk/clip_en.txt new file mode 100644 index 0000000000..4e6169c5e2 --- /dev/null +++ b/programs/system/clip/trunk/clip_en.txt @@ -0,0 +1,141 @@ +Global clipboard for Kolibri. Notes for developers. + +General info +Clipboard is implemented using daemon process and IPC messages. +To test, run @clip (the daemon process) and cliptest and debug board +or test2 (several instances). + +1. @clip daemon and its commands + +Process @clip creates no windows, but only listens for IPC messages. +Daemon supports 16 (MAX_FORMAT) buffers for different data types and up to +16,7 Mb (MAX_SIZE) data in each buffer (memory is allocated from heap). +Data format ID is a number from 0 to 65534. Value 65535 is reserved. + +When the daemon is started it terminates all other @clip instances. +Format of daemon's commands is following: + +[ Cmd: word | Format: word | Reserved: Dword | Data: ...] + +where Cmd is command id, +Format is data format id, +Reserved is unused (should be zero), +and Data is command-specific data. + +Following commands are implemented: + +1. Set Size. Define required size for buffer. This command causes daemon to +enlarge (if needed) its IPC buffer (there is currently no way to decrease +size of the buffer). +Data parameter: Dword with size of the data. +Command length: 12 bytes. + +2. Set. Data transfer. This command copies data to daemon's memory. +Data parameter: data to be copied. +Command length: 8 + (data size) bytes. + +3. Get Size. Get size of data with specified format id. This command causes +daemon to send in reply an IPC message telling the size of the data in the buffer. +If the buffer contains no data, 0 is returned. +Command length: 8 bytes. + +4. Get. Get the data with specified format id from the buffer. This command +causes daemon to send in reply an IPC message of required size with the data +from the buffer. If the buffer contains no data, no message is sent. +Command length: 8 bytes. + +5. Delete. Clear the buffer for specified format id. If 0xFFFF is specified, +all buffers are cleared. +Command length: 8 bytes. + +Source: @clip.asm. Uncomment the line: +;define DEBUG TRUE +and comment the next one to enable output on debug board. This is useful +for bugtracking. +DEFAULT_SIZE is initial IPC buffer size +MAX_SIZE is upper limit of IPC buffer size +MAX_FORMAT is number of formats daemon can store as the same time +(if more formats are copied, daemon will crash). +DELAY is pause between sending attepmts, 1/100 seconds. +ATTEMPT is number of sending attempts if target process is not ready. + + +2. clip.inc: function set for high-level communication with @clip. +Reading and writing is implemeted. + +Usage example: cliptest.asm (writes to debug board) and test2.asm. + +Following values should be defined: +DEFAULT_MASK = 7 ; Default event mask for current thread + +SEND_DELAY = 10 ; pause between sending attempts + +RECV_DELAY = 100 ; how much to wait for the answer + ; 1/100 secs + +ATTEMPT = 5 ; number of sending attempts + +Clip.inc contains the following functions: + +clipboard_init() - search of process @clip. May be called several times. +Returns 1 if successful and 0 if failed. + +clipboard_write(esi points to CLIP_BUFFER, +ax (word) - data format id) - copies data to buffer. +Uses 1-th and 2-nd commands. +Returns 1 if successful and 0 if failed. + +clipboard_read(esi points to CLIP_BUFFER, +ax (word) - data format id) - retrieves data from buffer. +Uses 3-rd and 4-th commands. +Returns 1 if successful, -1 if not enough data in receive buffer (which is +left unchanged in this case) and 0 if failed. +If eax = 1 or -1, edx contains real size of data in the buffer. + +Warning. If the application uses incoming IPC messages for other +purposes, process daemon's messages manually, because getting a different +format message will be ignored by clipboard_read. + +There are 2 low-level functions which may be called after clipboard_init: +_ipc_send (esi points to a common buffer, edx - byte count). +Sends an IPC message to daemon. The difference between this function and +function 60/2 is that _ipc_send makes several attempts if daemon is +unaccessible, with pause of SEND_DELAY/100 seconds. +Returns 1 if successful and 0 if failed. + +_ipc_recv(esi points to CLIP_BUFFER (см. далее), +edx = default event mask). +Waits for an IPC message for RECV_DELAY/100 seconds. +If successful, result is stored in esi. +Returns 1 if successful and 0 if failed. + +Format of buffer for clipboard: +CLIP_BUFFER +(+0) .size dd ? ; size of the buffer itself(N) + ; if you need to send less bytes that N + ; temporarily write the required size here + +(+4) .sys1 dd ? ; \ clip.inc uses these fields + ; - for internal values. You should not +(+8) .sys2 dd ? ; / modify them + +(+12) .data db N dup(?); buffer itself + +Good luck in programming and debugging! + +; barsuk, 21.08.2008 + + + + +@CLIP version 0.2. + +A capability of inserting text to applications not using @clip is added. +Function 72/1 is used to insert the text. +Only applications, using ascii mode for input are supported. + +To insert text press ctrl-alt-v hot key. +The text should be copied to 1-st buffer from @clip-compatible +application (example: test2). + +; 08.09.2008 \ No newline at end of file diff --git a/programs/system/clip/trunk/cliptest.ASM b/programs/system/clip/trunk/cliptest.ASM new file mode 100644 index 0000000000..a08bc953f9 --- /dev/null +++ b/programs/system/clip/trunk/cliptest.ASM @@ -0,0 +1,240 @@ +; <--- description ---> +; compiler: FASM +; name: Clipboard Client Demo +; version: 0.1 +; author: barsuk + + +; <--- include all MeOS stuff ---> +include "lang.inc" +include "..\..\..\MACROS.INC" + +TEST_SIZE = 100000 ; должно делиться на 4 + +IPC_buffer_size = TEST_SIZE + 0x10 +MAX_BUFFER = TEST_SIZE + 0x10 + +SEND_DELAY = 10 +RECV_DELAY = 100 + +ATTEMPT = 5 ; число попыток отправить сообщение демону, если + ; он занят + +; <--- start of MenuetOS application ---> +MEOS_APP_START + +define DEBUG TRUE +include "bdebug.inc" + +include "clip.inc" + +; <--- start of code ---> +CODE + + +;;; This is the entry point of the program + +begin: + +print "clipboard testing module loaded" + + mov eax, 5 + mov ebx, 100 ; 5 sec + int 0x40 + + call clipboard_init + or eax, eax + jz .err + + ; put the data into clip_buffer + + mov esi, test_line + mov edi, cb.data + mov ecx, cb.size + rep movsb + +print "init ok. @clip found." + + mov esi, cb + mov eax, 2 + call clipboard_write + or eax, eax + jz .err + +print "write test ok." + +; слегка очистим буфер, для надежности проверки + + mov dword [cb.data], 0 + +;jmp .exit + mov eax, 3 ; try to read bad data + mov esi, cb + mov edx, 7 + call clipboard_read + or eax, eax + jz .bad2 + cmp eax, -1 + jz .err + +.bad2: +print "unset data read ok 1." + mov eax, 88 ; -"- + mov esi, cb + mov edx, 7 + call clipboard_read + or eax, eax + jz .bad3 + cmp eax, -1 + jz .err + +.bad3: +print "unset data read ok 2." + +.read: + mov eax, 2 + mov esi, cb + mov edx, 7 + call clipboard_read ; read the string we've written + or eax, eax + jz .err + cmp eax, -1 + jz .err + +print "read ok o_O, data:" +outs cb.data +print "" + + mov eax, 5 + mov ebx, 200 + int 0x40 + + +; последняя проверка - на ресайз буфера. а именно, возьмем много данных +; и прокачаем их через буфер + + mov eax, 68 + mov ebx, 11 + int 0x40 + + mov eax, 68 + mov ebx, 12 + mov ecx, TEST_SIZE + 12 + int 0x40 + mov [mega_buf], eax + + mov dword [eax], TEST_SIZE + + lea edx, [eax + 12] + mov eax, 12131415 + shr ecx, 2 +.loop: + mov [edx], eax + add edx, 4 + dec ecx + jnz .loop + +print "mega buffer allocated and filled" + + ; now write it + + mov esi, [mega_buf] + mov eax, 69 + call clipboard_write + or eax, eax + jz .err + +print "mega buffer written" + mov eax, 5 + mov ebx, 100 + int 0x40 + + ; clear it. + + mov edx, [mega_buf] + add edx, 12 + xor eax, eax + mov ecx, TEST_SIZE/4 +.loopc: + mov [edx], eax + add edx, 4 + dec ecx + jnz .loopc + + ; now read + + mov eax, 69 + mov esi, [mega_buf] + mov edx, 7 + call clipboard_read ; read the string we've written + or eax, eax + jz .err + cmp eax, -1 + jz .err + +print "mega buf read" + + ; now compare + + mov edx, [mega_buf] + add edx, 12 + mov eax, 12131415 + mov ecx, TEST_SIZE/4 +.loopm: + cmp eax, [edx] + jnz .err + add edx, 4 + dec ecx + jnz .loopm + +print "deleting mega buf" + + mov eax, 69 + call clipboard_delete + or eax, eax + jz .err + + mov eax, 5 + mov ebx, 100 + int 0x40 + +print "if read error then all is ok" + + mov eax, 69 + mov esi, [mega_buf] + mov edx, 7 + call clipboard_read ; read the string we've written + or eax, eax + jz .reading + jmp .err + +.reading: + +print "test complete!" + +.exit: + or eax, -1 + int 0x40 + +.err: +print "error!" + jmp .exit + + +; <--- initialised data ---> +DATA + + CLIP_BUFFER cb, 256 + +; in_buf db 256 dup(0) +; out_buf db 'Cthulhu Fhtagn',0 + + test_line db 'Cthulhu Fhtagn',0 + mega_buf dd 0 + +; <--- uninitialised data ---> +UDATA + + +MEOS_APP_END +; <--- end of MenuetOS application ---> \ No newline at end of file diff --git a/programs/system/clip/trunk/test2.asm b/programs/system/clip/trunk/test2.asm new file mode 100644 index 0000000000..8e14d526ac --- /dev/null +++ b/programs/system/clip/trunk/test2.asm @@ -0,0 +1,272 @@ +; <--- description ---> +; compiler: FASM +; name: Clipboard test with GUI. +; version: 0.1 +; author: barsuk +; comments: uses edit_box by Maxxxx32, (old static version) + + +; <--- include all MeOS stuff ---> +include "lang.inc" +include "..\..\..\MACROS.INC" + +; <--- start of MenuetOS application ---> +MEOS_APP_START + +SEND_DELAY = 10 +RECV_DELAY = 100 +ATTEMPT = 5 + + +define DEBUG TRUE +include "bdebug.inc" + +include "editbox.inc" + +include "clip.inc" + +use_edit_box + +; <--- start of code ---> +CODE + + call clipboard_init + + call draw_window ; at first create and draw the window + +wait_event: ; main cycle + + mov edi, input_box + call edit_box.mouse + + mov eax, 10 + int 0x40 + + cmp eax, 1 ; if event == 1 + je redraw ; jump to redraw handler + cmp eax, 2 ; else if event == 2 + je key ; jump to key handler + cmp eax, 3 ; else if event == 3 + je button ; jump to button handler + + jmp wait_event ; else return to the start of main cycle + + +redraw: ; redraw event handler + call draw_window + jmp wait_event + + +key: ; key event handler + mov eax, 2 ; get key code + int 0x40 + + cmp ah, '0' + jb no_digit + cmp ah, '9' + ja no_digit + sub ah, '0' + mov byte [format_id], ah + call draw_window + jmp wait_event +no_digit: + + mov edi, input_box + call edit_box.key + + jmp wait_event + + + button: ; button event handler + mov eax, 17 ; get button identifier + int 0x40 + + cmp ah, 1 + jz exit + + cmp ah, 5 + jz copy + cmp ah, 6 + jz paste + jmp wait_event +copy: + mov esi, input_text + mov edi, buffer.data + mov ecx, [buffer.size] + rep movsb ; copy text to buffer + + mov edi, input_box + mov edx, [edi + 38] ; ed_size + xchg edx, [buffer.size] + movzx eax, byte [format_id] + mov esi, buffer + call clipboard_write + xchg edx, [buffer.size] + jmp wait_event + +paste: + mov esi, buffer + movzx eax, byte [format_id] + mov edx, 7 + call clipboard_read + + or eax, eax + jz wait_event + + cmp eax, -1 + jz wait_event + + or edx, edx + jz wait_event ; это все ошибки + + mov eax, input_box +;print "input box" +;dph1 [eax] +;dph1 [eax+4] +;dph1 [eax+8] +;dph1 [eax+12] +;dph1 [eax+16] +;dph1 [eax+20] +;dph1 [eax+24] + +;;;;jmp wait_event + + mov word [input_box + 36], ed_focus ; flags + + mov ecx, [input_box + 38] ; size +print "paste read ecx=size" + jecxz .noloop + mov edi, input_box + mov ah, 8 ; backspace +.loop: + call edit_box.key + dec ecx + jnz .loop +.noloop: + mov esi, buffer.data + mov ecx, edx +pregs +.loop2: + mov ah, [esi] + cmp ah, 0 + jz .done + call edit_box.key ; я бы себе руки отрезал за такое + ; но что делать, если иначе не получается? + inc esi + dec ecx + jnz .loop2 + +.done: +print "rest of data ecx" +pregs + call draw_window + jmp wait_event + +exit: + or eax, -1 ; exit application + int 0x40 + + + draw_window: + mov eax, 12 ; start drawing + mov ebx, 1 + int 0x40 + + mov eax, 0 ; create and draw the window + mov ebx, 100*65536+400 ; (window_cx)*65536+(window_sx) + mov ecx, 100*65536+250 ; (window_cy)*65536+(window_sy) + mov edx, 0x33ffffff ; work area color & window type 3 + mov edi, head + int 0x40 + + mov eax, 4 + mov ebx, 10 shl 16 + 10 + mov ecx, 0x80000000 + mov edx, text1 + int 0x40 + mov eax, 4 + mov ebx, 10 shl 16 + 20 + mov edx, text2 + int 0x40 + mov eax, 47 + mov ebx, 0x00020000 + movzx ecx, byte [format_id] + mov edx, 200 shl 16 + 20 + mov esi, 0 + int 0x40 + + mov eax, 8 + mov ebx, 10 shl 16 + 60 + mov ecx, 40 shl 16 + 15 + mov edx, 5 + mov esi, 0xd72189 + int 0x40 + mov eax, 4 + mov ebx, 12 shl 16 + 42 + mov ecx, 0x80000000 + mov edx, button1 + int 0x40 + + mov eax, 8 + mov ebx, 80 shl 16 + 60 + mov ecx, 40 shl 16 + 15 + mov edx, 6 + mov esi, 0xd72189 + int 0x40 + mov eax, 4 + mov ebx, 82 shl 16 + 42 + mov ecx, 0x80000000 + mov edx, button2 + int 0x40 + + mov edi, input_box + call edit_box.draw + +; mov eax, 4 +; mov ebx, 10 shl 16 + 90 +; mov ecx, 0 +; mov edx, input_text +; mov esi, [input_box + 38] +; int 0x40 +; mov eax, esi +;print "eax = edit etxt len" +;pregs + + + + mov eax, 12 ; finish drawing + mov ebx, 2 + int 0x40 + + ret + + + +; <--- initialised data ---> +DATA + + format_id db 1 + +; buffer db 256 dup(0) + + CLIP_BUFFER buffer, 256 + + input_text db 256 dup(0) + input_box edit_box 100,10,70,0xffffff,0,0x00aaaaaa,0,255,input_text + +; input_box edit_box 100,10,70,0xffffff,0,0xaaaaaa,0,\ +; 0xaaaaaa,0,255,input_text,ed_always_focus + + head db "Пример работы с буфером обмена",0 + text1 db "Нажимайте цифры 0-9 для смены id формата данных",0 + text2 db "Сейчас выбран id формата данных: ",0 + + button1 db "Копировать",0 + button2 db "Вставить",0 + +; <--- uninitialised data ---> +UDATA + + +MEOS_APP_END +; <--- end of MenuetOS application ---> \ No newline at end of file