1 #include "../../arcan_shmif.h"
2 #include "../../arcan_tui.h"
3 #include "../tui_int.h"
4 #include "../screen/libtsm.h"
5 #include "../screen/libtsm_int.h"
6 #include "../screen/utf8.c"
7
8 #include <stdio.h>
9 #include <inttypes.h>
10 #include <math.h>
11 #include <errno.h>
12
select_copy(struct tui_context * tui)13 static void select_copy(struct tui_context* tui)
14 {
15 char* sel = NULL;
16 ssize_t len;
17 /*
18 * The tsm_screen selection code is really icky and well-deserving of a
19 * rewrite. The 'select_townd' toggle here is that the selection function
20 * originally converted to utf8, while we work with UCS4 locally
21 */
22 int dst = atomic_load(&paste_destination);
23 if (tui->select_townd && -1 != dst){
24 len = tsm_screen_selection_copy(tui->screen, &sel, false);
25 if (!len || len <= 1)
26 return;
27
28 /* spinlock on block, we have already _Static_assert on PIPE_BUF */
29 while (len >= 4){
30 int rv = write(dst, sel, 4);
31 if (-1 == rv){
32 if (errno == EINVAL)
33 break;
34 else
35 continue;
36 }
37 len -= 4;
38 sel += 4;
39 }
40
41 /* always send a new line (even if it doesn't go through) */
42 uint32_t ch = '\n';
43 write(dst, &ch, 4);
44 return;
45 }
46
47 len = tsm_screen_selection_copy(tui->screen, &sel, true);
48 if (!sel || len <= 1)
49 return;
50
51 len--;
52
53 /* empty cells gets marked as NULL, but that would cut the copy short */
54 for (size_t i = 0; i < len; i++){
55 if (sel[i] == '\0')
56 sel[i] = ' ';
57 }
58
59 tui_clipboard_push(tui, sel, len);
60 free(sel);
61 }
62
page_up(struct tui_context * tui)63 static bool page_up(struct tui_context* tui)
64 {
65 if (!tui || (tui->flags & TUI_ALTERNATE))
66 return true;
67
68 tui->cursor_upd = true;
69 tui->cursor_off = true;
70 arcan_tui_scroll_up(tui, tui->rows);
71 return true;
72 }
73
page_down(struct tui_context * tui)74 static bool page_down(struct tui_context* tui)
75 {
76 if (!tui || (tui->flags & TUI_ALTERNATE))
77 return true;
78
79 if (tui->sbofs > 0){
80 tui->sbofs -= tui->rows;
81 tui->sbofs = tui->sbofs < 0 ? 0 : tui->sbofs;
82 tui->cursor_upd = true;
83 tui->cursor_off = true;
84 }
85
86 if (tui->sbofs <= 0){
87 tui->cursor_off = false;
88 tui->cursor_upd = true;
89 tsm_screen_sb_reset(tui->screen);
90 }
91
92 arcan_tui_scroll_down(tui, tui->rows);
93
94 return true;
95 }
96
mod_to_scroll(int mods,int screenh)97 static int mod_to_scroll(int mods, int screenh)
98 {
99 int rv = 1;
100 if (mods & ARKMOD_LSHIFT)
101 rv = screenh >> 1;
102 if (mods & ARKMOD_RSHIFT)
103 rv += screenh >> 1;
104 if (mods & ARKMOD_LCTRL)
105 rv += screenh >> 1;
106 if (mods & ARKMOD_RCTRL)
107 rv += screenh >> 1;
108 return rv;
109 }
110
copy_window(struct tui_context * tui)111 static bool copy_window(struct tui_context* tui)
112 {
113 /* if no pending copy-window request, make a copy of the active screen
114 * and spawn a dispatch thread for it */
115 if (tui->pending_copy_window)
116 return true;
117
118 if (tsm_screen_save(
119 tui->screen, true, &tui->pending_copy_window)){
120 arcan_shmif_enqueue(&tui->acon, &(struct arcan_event){
121 .ext.kind = ARCAN_EVENT(SEGREQ),
122 .ext.segreq.kind = SEGID_TUI,
123 .ext.segreq.id = REQID_COPYWINDOW
124 });
125 }
126
127 return true;
128 }
129
scroll_up(struct tui_context * tui)130 static bool scroll_up(struct tui_context* tui)
131 {
132 if (!tui || (tui->flags & TUI_ALTERNATE))
133 return true;
134
135 int nf = mod_to_scroll(tui->modifiers, tui->rows);
136 arcan_tui_scroll_up(tui, nf);
137 return true;
138 }
139
scroll_down(struct tui_context * tui)140 static bool scroll_down(struct tui_context* tui)
141 {
142 if (!tui || (tui->flags & TUI_ALTERNATE))
143 return true;
144
145 int nf = mod_to_scroll(tui->modifiers, tui->rows);
146 if (tui->sbofs > 0){
147 arcan_tui_scroll_down(tui, nf);
148 return true;
149 }
150 return false;
151 }
152
move_up(struct tui_context * tui)153 static bool move_up(struct tui_context* tui)
154 {
155 if (tui->scroll_lock){
156 page_up(tui);
157 return true;
158 }
159 /* else if (tui->modifiers & (TUIK_LMETA | TUIK_RMETA)){
160 if (tui->modifiers & (TUIK_LSHIFT | TUIK_RSHIFT))
161 page_up(tui);
162 else{
163 tsm_screen_sb_up(tui->screen, 1);
164 tui->sbofs += 1;
165 tui->dirty |= DIRTY_PENDING;
166 }
167 return true;
168 }
169 else */
170
171 return false;
172 }
173
move_down(struct tui_context * tui)174 static bool move_down(struct tui_context* tui)
175 {
176 if (tui->scroll_lock){
177 page_down(tui);
178 return true;
179 }
180 /*
181 * else if (tui->modifiers & (TUIK_LMETA | TUIK_RMETA)){
182 if (tui->modifiers & (TUIK_LSHIFT | TUIK_RSHIFT))
183 page_up(tui);
184 else{
185 tsm_screen_sb_down(tui->screen, 1);
186 tui->sbofs -= 1;
187 tui->dirty |= DIRTY_PENDING;
188 }
189 return true;
190 }
191 else
192 */
193 return false;
194 }
195
select_at(struct tui_context * tui)196 static bool select_at(struct tui_context* tui)
197 {
198 tsm_screen_selection_reset(tui->screen);
199 unsigned sx, sy, ex, ey;
200 int rv = tsm_screen_get_word(tui->screen,
201 tui->mouse_x, tui->mouse_y, &sx, &sy, &ex, &ey);
202
203 if (0 == rv){
204 tsm_screen_selection_reset(tui->screen);
205 tsm_screen_selection_start(tui->screen, sx, sy);
206 tsm_screen_selection_target(tui->screen, ex, ey);
207 select_copy(tui);
208 tui->dirty |= DIRTY_PARTIAL;
209 }
210
211 tui->in_select = false;
212 tui->dirty |= DIRTY_CURSOR;
213 return true;
214 }
215
select_row(struct tui_context * tui)216 static bool select_row(struct tui_context* tui)
217 {
218 tsm_screen_selection_reset(tui->screen);
219 int row = tsm_screen_get_cursor_y(tui->screen);
220 tsm_screen_selection_start(tui->screen, 0, row);
221 tsm_screen_selection_target(tui->screen, tui->cols-1, row);
222 select_copy(tui);
223 tui->dirty |= DIRTY_PARTIAL;
224 tui->dirty |= DIRTY_CURSOR;
225 tui->in_select = false;
226 return true;
227 }
228
scroll_lock(struct tui_context * tui)229 static bool scroll_lock(struct tui_context* tui)
230 {
231 tui->scroll_lock = !tui->scroll_lock;
232 if (!tui->scroll_lock){
233 tui->sbofs = 0;
234 tsm_screen_sb_reset(tui->screen);
235 tui->cursor_upd = true;
236 tui->cursor_off = false;
237 tui->dirty |= DIRTY_PARTIAL;
238 }
239 return true;
240 }
241
mouse_forward(struct tui_context * tui)242 static bool mouse_forward(struct tui_context* tui)
243 {
244 tui->mouse_forward = !tui->mouse_forward;
245 return true;
246 }
247
sel_sw(struct tui_context * tui)248 static bool sel_sw(struct tui_context* tui)
249 {
250 tui->select_townd = !tui->select_townd;
251 return true;
252 }
253
254 struct lent {
255 int ctx;
256 const char* lbl;
257 const char* descr;
258 uint8_t vsym[5];
259 bool(*ptr)(struct tui_context*);
260 uint16_t initial;
261 uint16_t modifiers;
262 };
263
264 #ifdef _DEBUG
265 #include <stdio.h>
dump_dbg(struct tui_context * tui)266 static bool dump_dbg(struct tui_context* tui)
267 {
268 /* dump front-delta, front, back to different files */
269 uint8_t* rbuf = NULL;
270 size_t rbuf_sz = 0;
271 tui_screen_tpack(tui,
272 (struct tpack_gen_opts){.full = true, .synch = false}, &rbuf, &rbuf_sz);
273
274 char buf[64];
275 snprintf(buf, 64, "/tmp/tui.%d.delta.front.tpack", getpid());
276 FILE* fout = fopen(buf, "w");
277 if (fout){
278 fwrite(rbuf, rbuf_sz, 1, fout);
279 fclose(fout);
280 }
281
282 tui_screen_tpack(tui,
283 (struct tpack_gen_opts){.full = true, .back = true}, &rbuf, &rbuf_sz);
284 snprintf(buf, 64, "/tmp/tui.%d.full.back.tpack", getpid());
285 fout = fopen(buf, "w");
286 if (fout){
287 fwrite(rbuf, rbuf_sz, 1, fout);
288 fclose(fout);
289 }
290
291 tui_screen_tpack(tui,
292 (struct tpack_gen_opts){0}, &rbuf, &rbuf_sz);
293 snprintf(buf, 64, "/tmp/tui.%d.full.front.tpack", getpid());
294 fout = fopen(buf, "w");
295 if (fout){
296 fwrite(rbuf, rbuf_sz, 1, fout);
297 fclose(fout);
298 }
299
300 return true;
301 }
302 #endif
303
304 static const struct lent labels[] = {
305 {1, "LINE_UP", "Scroll 1 row up", {}, scroll_up}, /* u+2191 */
306 {1, "LINE_DOWN", "Scroll 1 row down", {}, scroll_down}, /* u+2192 */
307 {1, "PAGE_UP", "Scroll one page up", {0xe2, 0x87, 0x9e}, page_up}, /* u+21de */
308 {1, "PAGE_DOWN", "Scroll one page down", {0xe2, 0x87, 0x9e}, page_down}, /* u+21df */
309 {0, "COPY_AT", "Copy word at cursor", {}, select_at}, /* u+21f8 */
310 {0, "COPY_ROW", "Copy cursor row", {}, select_row}, /* u+21a6 */
311 {0, "MOUSE_FORWARD", "Toggle mouse forwarding", {}, mouse_forward}, /* u+ */
312 {1, "SCROLL_LOCK", "Arrow- keys to pageup/down", {}, scroll_lock, TUIK_SCROLLLOCK}, /* u+ */
313 {1, "UP", "(scroll-lock) page up, UP keysym", {}, move_up}, /* u+ */
314 {1, "DOWN", "(scroll-lock) page down, DOWN keysym", {}, move_down}, /* u+ */
315 {0, "COPY_WND", "Copy window and scrollback", {}, copy_window}, /* u+ */
316 {2, "SELECT_TOGGLE", "Switch select destination (wnd, clipboard)", {}, sel_sw}, /* u+ */
317 #ifdef _DEBUG
318 {1, "DUMP", "Create a buffer/raster snapshot (/tmp/tui.pid.xxx)", {}, dump_dbg},
319 #endif
320 {0}
321 };
322
tui_expose_labels(struct tui_context * tui)323 void tui_expose_labels(struct tui_context* tui)
324 {
325 const struct lent* cur = labels;
326 arcan_event ev = {
327 .category = EVENT_EXTERNAL,
328 .ext.kind = ARCAN_EVENT(LABELHINT),
329 .ext.labelhint.idatatype = EVENT_IDATATYPE_DIGITAL
330 };
331
332 /* send an empty label first as a reset */
333 arcan_shmif_enqueue(&tui->acon, &ev);
334
335 /* then forward to a possible callback handler */
336 size_t ind = 0;
337 if (tui->handlers.query_label){
338 while (true){
339 struct tui_labelent dstlbl = {};
340 if (!tui->handlers.query_label(tui,
341 ind++, "ENG", "ENG", &dstlbl, tui->handlers.tag))
342 break;
343
344 snprintf(ev.ext.labelhint.label,
345 COUNT_OF(ev.ext.labelhint.label), "%s", dstlbl.label);
346 snprintf(ev.ext.labelhint.descr,
347 COUNT_OF(ev.ext.labelhint.descr), "%s", dstlbl.descr);
348 ev.ext.labelhint.subv = dstlbl.subv;
349 ev.ext.labelhint.idatatype = dstlbl.idatatype ? dstlbl.idatatype : EVENT_IDATATYPE_DIGITAL;
350 ev.ext.labelhint.modifiers = dstlbl.modifiers;
351 ev.ext.labelhint.initial = dstlbl.initial;
352 snprintf((char*)ev.ext.labelhint.vsym,
353 COUNT_OF(ev.ext.labelhint.vsym), "%s", dstlbl.vsym);
354 arcan_shmif_enqueue(&tui->acon, &ev);
355 }
356 }
357
358 /* expose a set of basic built-in controls shared by all users, and this is
359 * dependent, for now, on the mode of the context. The reason is that 'line-'
360 * oriented mode with it's special scrolling, selection etc. complexity should
361 * be refactored and pushed to a separate layer. */
362 while(cur->lbl){
363 switch(cur->ctx){
364 case 0:
365 /* all */
366 break;
367 case 1:
368 /* not in 'alternate' */
369 if (tui->flags & TUI_ALTERNATE){
370 cur++;
371 continue;
372 }
373 break;
374 case 2:
375 /* only when not in copywnd */
376 if (!tui->subseg){
377 cur++;
378 continue;
379 }
380 break;
381 }
382
383 snprintf(ev.ext.labelhint.label,
384 COUNT_OF(ev.ext.labelhint.label), "%s", cur->lbl);
385 snprintf(ev.ext.labelhint.descr,
386 COUNT_OF(ev.ext.labelhint.descr), "%s", cur->descr);
387 snprintf((char*)ev.ext.labelhint.vsym,
388 COUNT_OF(ev.ext.labelhint.vsym), "%s", cur->vsym);
389 cur++;
390
391 ev.ext.labelhint.initial = cur->initial;
392 ev.ext.labelhint.modifiers = cur->modifiers;
393 arcan_shmif_enqueue(&tui->acon, &ev);
394 }
395 }
396
update_mods(int mods,int sym,bool pressed)397 static int update_mods(int mods, int sym, bool pressed)
398 {
399 if (pressed)
400 switch(sym){
401 case TUIK_LSHIFT: return mods | ARKMOD_LSHIFT;
402 case TUIK_RSHIFT: return mods | ARKMOD_RSHIFT;
403 case TUIK_LCTRL: return mods | ARKMOD_LCTRL;
404 case TUIK_RCTRL: return mods | ARKMOD_RCTRL;
405 case TUIK_COMPOSE:
406 case TUIK_LMETA: return mods | ARKMOD_LMETA;
407 case TUIK_RMETA: return mods | ARKMOD_RMETA;
408 default:
409 return mods;
410 }
411 else
412 switch(sym){
413 case TUIK_LSHIFT: return mods & (~ARKMOD_LSHIFT);
414 case TUIK_RSHIFT: return mods & (~ARKMOD_RSHIFT);
415 case TUIK_LCTRL: return mods & (~ARKMOD_LCTRL);
416 case TUIK_RCTRL: return mods & (~ARKMOD_RCTRL);
417 case TUIK_COMPOSE:
418 case TUIK_LMETA: return mods & (~ARKMOD_LMETA);
419 case TUIK_RMETA: return mods & (~ARKMOD_RMETA);
420 default:
421 return mods;
422 }
423 }
424
consume_label(struct tui_context * tui,arcan_ioevent * ioev,const char * label)425 static bool consume_label(struct tui_context* tui,
426 arcan_ioevent* ioev, const char* label)
427 {
428 const struct lent* cur = labels;
429
430 /* priority to our normal label handlers, and if those fail, forward */
431 while(cur->lbl){
432 if (strcmp(label, cur->lbl) == 0){
433 if (cur->ptr(tui))
434 return true;
435 else
436 break;
437 }
438 cur++;
439 }
440
441 bool res = false;
442 if (tui->handlers.input_label){
443 res |= tui->handlers.input_label(tui, label, true, tui->handlers.tag);
444
445 /* also send release if the forward was ok */
446 if (res)
447 tui->handlers.input_label(tui, label, false, tui->handlers.tag);
448 }
449
450 return res;
451 }
452
forward_mouse(struct tui_context * tui)453 static bool forward_mouse(struct tui_context* tui)
454 {
455 bool forward = tui->mouse_forward;
456 if (
457 !(tui->flags & TUI_MOUSE_FULL) &&
458 (tui->modifiers & (TUIM_LCTRL | TUIM_RCTRL))){
459 return !forward;
460 }
461 return forward;
462 }
463
tui_input_event(struct tui_context * tui,arcan_ioevent * ioev,const char * label)464 void tui_input_event(
465 struct tui_context* tui, arcan_ioevent* ioev, const char* label)
466 {
467 if (ioev->datatype == EVENT_IDATATYPE_TRANSLATED){
468 bool pressed = ioev->input.translated.active;
469 int sym = ioev->input.translated.keysym;
470 int oldm = tui->modifiers;
471 tui->modifiers = update_mods(tui->modifiers, sym, pressed);
472
473 /* note that after this point we always fake 'release' and forward as a
474 * press->release on the same label within consume label */
475 if (!pressed)
476 return;
477
478 if (tui->in_select){
479 tui->in_select = false;
480 tsm_screen_selection_reset(tui->screen);
481 }
482 tui->inact_timer = -4;
483 if (label[0] && consume_label(tui, ioev, label))
484 return;
485
486 /* modifiers doesn't get set for the symbol itself which is a problem
487 * for when we want to forward modifier data to another handler like mbtn */
488 if (sym >= 300 && sym <= 314)
489 return;
490
491 /* reset scrollback on normal input */
492 if (oldm == tui->modifiers && tui->sbofs != 0){
493 tui->cursor_upd = true;
494 tui->cursor_off = false;
495 tui->sbofs = 0;
496 tsm_screen_sb_reset(tui->screen);
497 tui->dirty |= DIRTY_PARTIAL;
498 }
499
500 /* check the incoming utf8 if it's valid, if so forward and if the handler
501 * consumed the value, leave the function */
502 int len = 0;
503 bool valid = true;
504 uint32_t codepoint = 0, state = 0;
505 while (len < 5 && ioev->input.translated.utf8[len]){
506 if (UTF8_REJECT == utf8_decode(&state, &codepoint,
507 ioev->input.translated.utf8[len])){
508 valid = false;
509 break;
510 }
511 len++;
512 }
513
514 /* disallow the private-use area */
515 if ((codepoint >= 0xe000 && codepoint <= 0xf8ff))
516 valid = false;
517
518 if (valid && ioev->input.translated.utf8[0] && tui->handlers.input_utf8){
519 if (tui->handlers.input_utf8 && tui->handlers.input_utf8(tui,
520 (const char*)ioev->input.translated.utf8,
521 len, tui->handlers.tag))
522 return;
523 }
524
525 /* otherwise, forward as much of the key as we know */
526 if (tui->handlers.input_key)
527 tui->handlers.input_key(tui,
528 sym,
529 ioev->input.translated.scancode,
530 ioev->input.translated.modifiers,
531 ioev->subid, tui->handlers.tag
532 );
533 }
534 else if (ioev->devkind == EVENT_IDEVKIND_MOUSE){
535 if (ioev->datatype == EVENT_IDATATYPE_ANALOG){
536 if (ioev->subid == 0){
537 tui->mouse_x = ioev->input.analog.axisval[0] / tui->cell_w;
538 }
539 else if (ioev->subid == 1){
540 int yv = ioev->input.analog.axisval[0];
541 tui->mouse_y = yv / tui->cell_h;
542
543 bool upd = false;
544 if (tui->mouse_x != tui->lm_x){
545 tui->lm_x = tui->mouse_x;
546 upd = true;
547 }
548 if (tui->mouse_y != tui->lm_y){
549 tui->lm_y = tui->mouse_y;
550 upd = true;
551 }
552
553 if (forward_mouse(tui) && tui->handlers.input_mouse_motion){
554 if (upd)
555 tui->handlers.input_mouse_motion(tui, false,
556 tui->mouse_x, tui->mouse_y, tui->modifiers, tui->handlers.tag);
557 return;
558 }
559
560 if (!tui->in_select)
561 return;
562
563 /* we use the upper / lower regions as triggers for scrollback + selection,
564 * with a magnitude based on how far "off" we are */
565 if (yv < 0.3 * tui->cell_h)
566 tui->scrollback = -1 * (1 + yv / tui->cell_h);
567 else if (yv > tui->rows * tui->cell_h + 0.3 * tui->cell_h)
568 tui->scrollback = 1 + (yv - tui->rows * tui->cell_h) / tui->cell_h;
569 else
570 tui->scrollback = 0;
571
572 /* in select and drag negative in window or half-size - then use ticker
573 * to scroll and an accelerated scrollback */
574 if (upd){
575 tsm_screen_selection_target(tui->screen, tui->lm_x, tui->lm_y);
576 tui->dirty |= DIRTY_PARTIAL | DIRTY_CURSOR;
577 }
578 /* in select? check if motion tile is different than old, if so,
579 * tsm_selection_target */
580 }
581 }
582 /* press? press-point tsm_screen_selection_start,
583 * release and press-tile ~= release_tile? copy */
584 else if (ioev->datatype == EVENT_IDATATYPE_DIGITAL){
585 if (ioev->subid){
586 if (ioev->input.digital.active)
587 tui->mouse_btnmask |= (1 << (ioev->subid-1));
588 else
589 tui->mouse_btnmask &= ~(1 << (ioev->subid-1));
590 }
591 if (forward_mouse(tui) && tui->handlers.input_mouse_button){
592 tui->handlers.input_mouse_button(tui, tui->mouse_x,
593 tui->mouse_y, ioev->subid, ioev->input.digital.active,
594 tui->modifiers, tui->handlers.tag
595 );
596 return;
597 }
598
599 if (ioev->flags & ARCAN_IOFL_GESTURE){
600 if (strcmp(ioev->label, "dblclick") == 0){
601 /* select row if double doubleclick */
602 if (tui->last_dbl_x == tui->mouse_x &&
603 tui->last_dbl_y == tui->mouse_y){
604 tsm_screen_selection_reset(tui->screen);
605 tsm_screen_selection_start(tui->screen, 0, tui->mouse_y);
606 tsm_screen_selection_target(
607 tui->screen, tui->cols-1, tui->mouse_y);
608 select_copy(tui);
609 tui->dirty |= DIRTY_PARTIAL;
610 tui->in_select = false;
611 }
612 /* select word */
613 else{
614 unsigned sx, sy, ex, ey;
615 sx = sy = ex = ey = 0;
616 int rv = tsm_screen_get_word(tui->screen,
617 tui->mouse_x, tui->mouse_y, &sx, &sy, &ex, &ey);
618 if (0 == rv){
619 tsm_screen_selection_reset(tui->screen);
620 tsm_screen_selection_start(tui->screen, sx, sy);
621 tsm_screen_selection_target(tui->screen, ex, ey);
622 select_copy(tui);
623 tui->dirty |= DIRTY_PARTIAL | DIRTY_CURSOR;
624 tui->in_select = false;
625 }
626 }
627
628 tui->last_dbl_x = tui->mouse_x;
629 tui->last_dbl_y = tui->mouse_y;
630 }
631 else if (strcmp(ioev->label, "click") == 0){
632 /* TODO: forward to cfg->nal? */
633 }
634 return;
635 }
636
637 /* scroll or select?
638 * NOTE: should also consider a way to specify magnitude */
639 if (ioev->subid == TUIBTN_WHEEL_UP){
640 if (ioev->input.digital.active){
641
642 /* normal ALTSCREEN wheel doesn't really make sense, unless in
643 * drag-select, map that to stepping selected row up/down?)
644 * clients can still switch to manual mouse mode to get the other behavior */
645 if ((tui->flags & TUI_ALTERNATE)){
646 tui->handlers.input_key(tui,
647 ((tui->modifiers & (ARKMOD_LSHIFT | ARKMOD_RSHIFT)) ? TUIK_PAGEUP : TUIK_UP),
648 ioev->input.translated.scancode,
649 0,
650 ioev->subid, tui->handlers.tag
651 );
652 }
653 else
654 scroll_up(tui);
655 }
656 }
657 else if (ioev->subid == TUIBTN_WHEEL_DOWN){
658 if (ioev->input.digital.active){
659 if ((tui->flags & TUI_ALTERNATE)){
660 tui->handlers.input_key(tui,
661 ((tui->modifiers & (ARKMOD_LSHIFT | ARKMOD_RSHIFT)) ? TUIK_PAGEDOWN : TUIK_DOWN),
662 ioev->input.translated.scancode,
663 0,
664 ioev->subid, tui->handlers.tag
665 );
666 }
667 else
668 scroll_down(tui);
669 }
670 }
671 else if (ioev->input.digital.active){
672 tsm_screen_selection_start(tui->screen, tui->mouse_x, tui->mouse_y);
673 tui->bsel_x = tui->mouse_x;
674 tui->bsel_y = tui->mouse_y;
675 tui->lm_x = tui->mouse_x;
676 tui->lm_y = tui->mouse_y;
677 tui->in_select = true;
678 }
679 else{
680 if (tui->mouse_x != tui->bsel_x || tui->mouse_y != tui->bsel_y)
681 select_copy(tui);
682
683 tsm_screen_selection_reset(tui->screen);
684 tui->in_select = false;
685 tui->dirty |= DIRTY_PARTIAL | DIRTY_CURSOR;
686 }
687 }
688 else if (tui->handlers.input_misc)
689 tui->handlers.input_misc(tui, ioev, tui->handlers.tag);
690 }
691 }
692