Start work on DnD protocol
This commit is contained in:
parent
4483a6c110
commit
2898324047
9 changed files with 228 additions and 1 deletions
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
from kitty.conf.types import Definition, expand_opt_references # noqa
|
from kitty.conf.types import Definition, expand_opt_references # noqa
|
||||||
from kitty.constants import str_version, website_url # noqa
|
from kitty.constants import str_version, website_url # noqa
|
||||||
from kitty.fast_data_types import Shlex, TEXT_SIZE_CODE # noqa
|
from kitty.fast_data_types import DND_CODE, Shlex, TEXT_SIZE_CODE # noqa
|
||||||
|
|
||||||
# config {{{
|
# config {{{
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
@ -121,6 +121,7 @@ def go_version(go_mod_path: str) -> str: # {{{
|
||||||
'_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',
|
'_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',
|
||||||
'_build_go_version': go_version('../go.mod'),
|
'_build_go_version': go_version('../go.mod'),
|
||||||
'_text_size_code': str(TEXT_SIZE_CODE),
|
'_text_size_code': str(TEXT_SIZE_CODE),
|
||||||
|
'_dnd_code': str(DND_CODE),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,7 @@ def parsers() -> None:
|
||||||
}
|
}
|
||||||
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
||||||
write_header(text, 'kitty/parse-graphics-command.h')
|
write_header(text, 'kitty/parse-graphics-command.h')
|
||||||
|
|
||||||
keymap = {
|
keymap = {
|
||||||
'w': ('width', 'uint'),
|
'w': ('width', 'uint'),
|
||||||
's': ('scale', 'uint'),
|
's': ('scale', 'uint'),
|
||||||
|
|
@ -329,6 +330,15 @@ def parsers() -> None:
|
||||||
payload_is_base64=False, start_parsing_at=0, field_sep=':')
|
payload_is_base64=False, start_parsing_at=0, field_sep=':')
|
||||||
write_header(text, 'kitty/parse-multicell-command.h')
|
write_header(text, 'kitty/parse-multicell-command.h')
|
||||||
|
|
||||||
|
keymap = {
|
||||||
|
't': ('type', flag('ae')),
|
||||||
|
'm': ('more', 'uint'),
|
||||||
|
}
|
||||||
|
text = generate(
|
||||||
|
'parse_dnd_code', 'screen_handle_dnd_command', 'dnd_command', keymap, 'DnDCommand',
|
||||||
|
payload_is_base64=True, start_parsing_at=0, field_sep=':')
|
||||||
|
write_header(text, 'kitty/parse-dnd-command.h')
|
||||||
|
|
||||||
|
|
||||||
def main(args: list[str]=sys.argv) -> None:
|
def main(args: list[str]=sys.argv) -> None:
|
||||||
parsers()
|
parsers()
|
||||||
|
|
|
||||||
|
|
@ -235,3 +235,5 @@
|
||||||
#define PENDING_MODE 2026
|
#define PENDING_MODE 2026
|
||||||
// Text size OSC number
|
// Text size OSC number
|
||||||
#define TEXT_SIZE_CODE 66
|
#define TEXT_SIZE_CODE 66
|
||||||
|
// Drag and drop protocol
|
||||||
|
#define DND_CODE 72
|
||||||
|
|
|
||||||
|
|
@ -864,6 +864,7 @@ PyInit_fast_data_types(void) {
|
||||||
PyModule_AddIntMacro(m, ESC_DCS);
|
PyModule_AddIntMacro(m, ESC_DCS);
|
||||||
PyModule_AddIntMacro(m, ESC_PM);
|
PyModule_AddIntMacro(m, ESC_PM);
|
||||||
PyModule_AddIntMacro(m, TEXT_SIZE_CODE);
|
PyModule_AddIntMacro(m, TEXT_SIZE_CODE);
|
||||||
|
PyModule_AddIntMacro(m, DND_CODE);
|
||||||
PyModule_AddIntMacro(m, COLOR_NOT_SET);
|
PyModule_AddIntMacro(m, COLOR_NOT_SET);
|
||||||
PyModule_AddIntMacro(m, COLOR_IS_SPECIAL);
|
PyModule_AddIntMacro(m, COLOR_IS_SPECIAL);
|
||||||
PyModule_AddIntMacro(m, COLOR_IS_INDEX);
|
PyModule_AddIntMacro(m, COLOR_IS_INDEX);
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,7 @@ WINDOW_MAXIMIZED: int
|
||||||
WINDOW_MINIMIZED: int
|
WINDOW_MINIMIZED: int
|
||||||
WINDOW_HIDDEN: int
|
WINDOW_HIDDEN: int
|
||||||
TEXT_SIZE_CODE: int
|
TEXT_SIZE_CODE: int
|
||||||
|
DND_CODE: int
|
||||||
TOP_EDGE: int
|
TOP_EDGE: int
|
||||||
BOTTOM_EDGE: int
|
BOTTOM_EDGE: int
|
||||||
LEFT_EDGE: int
|
LEFT_EDGE: int
|
||||||
|
|
|
||||||
190
kitty/parse-dnd-command.h
Normal file
190
kitty/parse-dnd-command.h
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
// This file is generated by apc_parsers.py do not edit!
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
static inline void parse_dnd_code(PS *self, uint8_t *parser_buf,
|
||||||
|
const size_t parser_buf_pos) {
|
||||||
|
unsigned int pos = 0;
|
||||||
|
|
||||||
|
enum PARSER_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE, PAYLOAD };
|
||||||
|
enum PARSER_STATES state = KEY, value_state = FLAG;
|
||||||
|
DnDCommand g = {0};
|
||||||
|
unsigned int i, code;
|
||||||
|
uint64_t lcode;
|
||||||
|
int64_t accumulator;
|
||||||
|
bool is_negative;
|
||||||
|
(void)is_negative;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
enum KEYS { type = 't', more = 'm' };
|
||||||
|
|
||||||
|
enum KEYS key = 'a';
|
||||||
|
if (parser_buf[pos] == ';')
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
|
||||||
|
while (pos < parser_buf_pos) {
|
||||||
|
switch (state) {
|
||||||
|
case KEY:
|
||||||
|
key = parser_buf[pos++];
|
||||||
|
state = EQUAL;
|
||||||
|
switch (key) {
|
||||||
|
case type:
|
||||||
|
value_state = FLAG;
|
||||||
|
break;
|
||||||
|
case more:
|
||||||
|
value_state = UINT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
REPORT_ERROR(
|
||||||
|
"Malformed DnDCommand control block, invalid key character: 0x%x",
|
||||||
|
key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EQUAL:
|
||||||
|
if (parser_buf[pos++] != '=') {
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, no = after key, "
|
||||||
|
"found: 0x%x instead",
|
||||||
|
parser_buf[pos - 1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = value_state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLAG:
|
||||||
|
switch (key) {
|
||||||
|
|
||||||
|
case type: {
|
||||||
|
g.type = parser_buf[pos++];
|
||||||
|
if (g.type != 'a' && g.type != 'e') {
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, unknown flag value "
|
||||||
|
"for type: 0x%x",
|
||||||
|
g.type);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INT:
|
||||||
|
#define READ_UINT \
|
||||||
|
for (i = pos, accumulator = 0; i < MIN(parser_buf_pos, pos + 10); i++) { \
|
||||||
|
int64_t n = parser_buf[i] - '0'; \
|
||||||
|
if (n < 0 || n > 9) \
|
||||||
|
break; \
|
||||||
|
accumulator += n * digit_multipliers[i - pos]; \
|
||||||
|
} \
|
||||||
|
if (i == pos) { \
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, expecting an integer " \
|
||||||
|
"value for key: %c", \
|
||||||
|
key & 0xFF); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
lcode = accumulator / digit_multipliers[i - pos - 1]; \
|
||||||
|
pos = i; \
|
||||||
|
if (lcode > UINT32_MAX) { \
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, number is too large"); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
code = lcode;
|
||||||
|
|
||||||
|
is_negative = false;
|
||||||
|
if (parser_buf[pos] == '-') {
|
||||||
|
is_negative = true;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
#define I(x) \
|
||||||
|
case x: \
|
||||||
|
g.x = is_negative ? 0 - (int32_t)code : (int32_t)code; \
|
||||||
|
break
|
||||||
|
READ_UINT;
|
||||||
|
switch (key) {
|
||||||
|
;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef I
|
||||||
|
case UINT:
|
||||||
|
READ_UINT;
|
||||||
|
#define U(x) \
|
||||||
|
case x: \
|
||||||
|
g.x = code; \
|
||||||
|
break
|
||||||
|
switch (key) {
|
||||||
|
U(more);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef U
|
||||||
|
#undef READ_UINT
|
||||||
|
|
||||||
|
case AFTER_VALUE:
|
||||||
|
switch (parser_buf[pos++]) {
|
||||||
|
default:
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, expecting a : or "
|
||||||
|
"semi-colon after a value, found: 0x%x",
|
||||||
|
parser_buf[pos - 1]);
|
||||||
|
return;
|
||||||
|
case ':':
|
||||||
|
state = KEY;
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
state = PAYLOAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAYLOAD: {
|
||||||
|
sz = parser_buf_pos - pos;
|
||||||
|
g.payload_sz = MAX(BUF_EXTRA, sz);
|
||||||
|
if (!base64_decode8(parser_buf + pos, sz, parser_buf, &g.payload_sz)) {
|
||||||
|
g.payload_sz = MAX(BUF_EXTRA, sz);
|
||||||
|
REPORT_ERROR("Failed to parse DnDCommand command payload with error: "
|
||||||
|
" invalid base64 data in chunk of size: %zu with output "
|
||||||
|
"buffer size: %zu",
|
||||||
|
sz, g.payload_sz);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos = parser_buf_pos;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
} // end switch
|
||||||
|
} // end while
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case EQUAL:
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, no = after key");
|
||||||
|
return;
|
||||||
|
case INT:
|
||||||
|
case UINT:
|
||||||
|
REPORT_ERROR(
|
||||||
|
"Malformed DnDCommand control block, expecting an integer value");
|
||||||
|
return;
|
||||||
|
case FLAG:
|
||||||
|
REPORT_ERROR("Malformed DnDCommand control block, expecting a flag value");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
REPORT_VA_COMMAND("K s {sc sI ss#}", self->window_id, "dnd_command",
|
||||||
|
|
||||||
|
"type", g.type,
|
||||||
|
|
||||||
|
"more", (unsigned int)g.more,
|
||||||
|
|
||||||
|
"", (char *)parser_buf, g.payload_sz);
|
||||||
|
|
||||||
|
screen_handle_dnd_command(self->screen, &g, parser_buf);
|
||||||
|
}
|
||||||
|
|
@ -1507,6 +1507,18 @@ screen_dirty_line_graphics(Screen *self, const unsigned int top, const unsigned
|
||||||
grman_remove_cell_images(main_buf ? self->main_grman : self->alt_grman, top, bottom);
|
grman_remove_cell_images(main_buf ? self->main_grman : self->alt_grman, top, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *payload) {
|
||||||
|
if (!self->window_id) return;
|
||||||
|
switch(cmd->type) {
|
||||||
|
case 'a':
|
||||||
|
(void)payload;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload) {
|
screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload) {
|
||||||
unsigned int x = self->cursor->x, y = self->cursor->y;
|
unsigned int x = self->cursor->x, y = self->cursor->y;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@
|
||||||
|
|
||||||
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
|
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
|
||||||
|
|
||||||
|
typedef struct DnDCommand {
|
||||||
|
char type;
|
||||||
|
unsigned more;
|
||||||
|
size_t payload_sz;
|
||||||
|
} DnDCommand;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM, mCOLOR_PREFERENCE_NOTIFICATION,
|
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM, mCOLOR_PREFERENCE_NOTIFICATION,
|
||||||
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE, mHANDLE_TERMIOS_SIGNALS, mINBAND_RESIZE_NOTIFICATION,
|
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE, mHANDLE_TERMIOS_SIGNALS, mINBAND_RESIZE_NOTIFICATION,
|
||||||
|
|
@ -290,6 +296,7 @@ void set_active_hyperlink(Screen*, char*, char*);
|
||||||
hyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type);
|
hyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type);
|
||||||
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
|
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
|
||||||
void screen_handle_multicell_command(Screen *self, const MultiCellCommand *cmd, const uint8_t *payload);
|
void screen_handle_multicell_command(Screen *self, const MultiCellCommand *cmd, const uint8_t *payload);
|
||||||
|
void screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *payload);
|
||||||
bool screen_open_url(Screen*);
|
bool screen_open_url(Screen*);
|
||||||
bool screen_set_last_visited_prompt(Screen*, index_type);
|
bool screen_set_last_visited_prompt(Screen*, index_type);
|
||||||
bool screen_select_cmd_output(Screen*, index_type);
|
bool screen_select_cmd_output(Screen*, index_type);
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@ find_st_terminator(PS *self, size_t *end_pos) {
|
||||||
// OSC {{{
|
// OSC {{{
|
||||||
|
|
||||||
#include "parse-multicell-command.h"
|
#include "parse-multicell-command.h"
|
||||||
|
#include "parse-dnd-command.h"
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_osc_52(PS *self) {
|
is_osc_52(PS *self) {
|
||||||
|
|
@ -574,6 +575,8 @@ dispatch_osc(PS *self, uint8_t *buf, size_t limit, bool is_extended_osc) {
|
||||||
case TEXT_SIZE_CODE:
|
case TEXT_SIZE_CODE:
|
||||||
parse_multicell_code(self, buf + i, limit - i);
|
parse_multicell_code(self, buf + i, limit - i);
|
||||||
break;
|
break;
|
||||||
|
case DND_CODE:
|
||||||
|
parse_dnd_code(self, buf + i, limit - i); break;
|
||||||
case 133:
|
case 133:
|
||||||
#ifdef DUMP_COMMANDS
|
#ifdef DUMP_COMMANDS
|
||||||
START_DISPATCH
|
START_DISPATCH
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue