st.c (105854B)
1 /* See LICENSE for license details. */ 2 #include <ctype.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 /* for BTN_* definitions */ 7 #include <linux/input.h> 8 #include <locale.h> 9 #include <pwd.h> 10 #include <stdarg.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <signal.h> 15 #include <stdint.h> 16 #include <sys/ioctl.h> 17 #include <sys/mman.h> 18 #include <sys/select.h> 19 #include <sys/stat.h> 20 #include <sys/time.h> 21 #include <sys/types.h> 22 #include <sys/wait.h> 23 #include <termios.h> 24 #include <time.h> 25 #include <unistd.h> 26 #include <libgen.h> 27 #include <wayland-client.h> 28 #include <wayland-cursor.h> 29 #include <xkbcommon/xkbcommon.h> 30 #include <wld/wld.h> 31 #include <wld/wayland.h> 32 #include <fontconfig/fontconfig.h> 33 #include <wchar.h> 34 35 #include "arg.h" 36 #include "xdg-shell-client-protocol.h" 37 #include "text-input-unstable-v3-client-protocol.h" 38 39 char *argv0; 40 41 #if defined(__linux) 42 #include <pty.h> 43 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 44 #include <util.h> 45 #elif defined(__FreeBSD__) || defined(__DragonFly__) 46 #include <libutil.h> 47 #endif 48 #if defined(__OpenBSD__) 49 #include <sys/sysctl.h> 50 #endif 51 52 53 /* Arbitrary sizes */ 54 #define UTF_INVALID 0xFFFD 55 #define UTF_SIZ 4 56 #define ESC_BUF_SIZ (128*UTF_SIZ) 57 #define ESC_ARG_SIZ 16 58 #define STR_BUF_SIZ ESC_BUF_SIZ 59 #define STR_ARG_SIZ ESC_ARG_SIZ 60 #define DRAW_BUF_SIZ 20*1024 61 #define XK_ANY_MOD UINT_MAX 62 #define XK_NO_MOD 0 63 #define XK_SWITCH_MOD (1<<13) 64 65 #define MOD_MASK_ANY UINT_MAX 66 #define MOD_MASK_NONE 0 67 #define MOD_MASK_CTRL (1<<0) 68 #define MOD_MASK_ALT (1<<1) 69 #define MOD_MASK_SHIFT (1<<2) 70 #define MOD_MASK_LOGO (1<<3) 71 72 #define AXIS_VERTICAL WL_POINTER_AXIS_VERTICAL_SCROLL 73 #define AXIS_HORIZONTAL WL_POINTER_AXIS_HORIZONTAL_SCROLL 74 75 /* macros */ 76 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 77 #define MAX(a, b) ((a) < (b) ? (b) : (a)) 78 #define LEN(a) (sizeof(a) / sizeof(a)[0]) 79 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 80 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 81 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) 82 #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') 83 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) 84 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) 85 #define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) 86 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 87 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ 88 (a).bg != (b).bg) 89 #define IS_SET(flag) ((term.mode & (flag)) != 0) 90 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ 91 (t1.tv_nsec-t2.tv_nsec)/1E6) 92 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 93 94 #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) 95 #define IS_TRUECOL(x) (1 << 24 & (x)) 96 #define TRUERED(x) (((x) & 0xff0000) >> 8) 97 #define TRUEGREEN(x) (((x) & 0xff00)) 98 #define TRUEBLUE(x) (((x) & 0xff) << 8) 99 100 /* constants */ 101 #define ISO14755CMD "dmenu -b -p codepoint: </dev/null" 102 #define BACKSPACE "\b" 103 104 enum glyph_attribute { 105 ATTR_NULL = 0, 106 ATTR_BOLD = 1 << 0, 107 ATTR_FAINT = 1 << 1, 108 ATTR_ITALIC = 1 << 2, 109 ATTR_UNDERLINE = 1 << 3, 110 ATTR_BLINK = 1 << 4, 111 ATTR_REVERSE = 1 << 5, 112 ATTR_INVISIBLE = 1 << 6, 113 ATTR_STRUCK = 1 << 7, 114 ATTR_WRAP = 1 << 8, 115 ATTR_WIDE = 1 << 9, 116 ATTR_WDUMMY = 1 << 10, 117 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, 118 }; 119 120 enum cursor_movement { 121 CURSOR_SAVE, 122 CURSOR_LOAD 123 }; 124 125 enum cursor_state { 126 CURSOR_DEFAULT = 0, 127 CURSOR_WRAPNEXT = 1, 128 CURSOR_ORIGIN = 2 129 }; 130 131 enum term_mode { 132 MODE_WRAP = 1 << 0, 133 MODE_INSERT = 1 << 1, 134 MODE_APPKEYPAD = 1 << 2, 135 MODE_ALTSCREEN = 1 << 3, 136 MODE_CRLF = 1 << 4, 137 MODE_MOUSEBTN = 1 << 5, 138 MODE_MOUSEMOTION = 1 << 6, 139 MODE_REVERSE = 1 << 7, 140 MODE_KBDLOCK = 1 << 8, 141 MODE_HIDE = 1 << 9, 142 MODE_ECHO = 1 << 10, 143 MODE_APPCURSOR = 1 << 11, 144 MODE_MOUSESGR = 1 << 12, 145 MODE_8BIT = 1 << 13, 146 MODE_BLINK = 1 << 14, 147 MODE_FBLINK = 1 << 15, 148 MODE_FOCUS = 1 << 16, 149 MODE_MOUSEX10 = 1 << 17, 150 MODE_MOUSEMANY = 1 << 18, 151 MODE_BRCKTPASTE = 1 << 19, 152 MODE_PRINT = 1 << 20, 153 MODE_UTF8 = 1 << 21, 154 MODE_SIXEL = 1 << 22, 155 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ 156 |MODE_MOUSEMANY, 157 }; 158 159 enum charset { 160 CS_GRAPHIC0, 161 CS_GRAPHIC1, 162 CS_UK, 163 CS_USA, 164 CS_MULTI, 165 CS_GER, 166 CS_FIN 167 }; 168 169 enum escape_state { 170 ESC_START = 1, 171 ESC_CSI = 2, 172 ESC_STR = 4, /* OSC, PM, APC */ 173 ESC_ALTCHARSET = 8, 174 ESC_STR_END = 16, /* a final string was encountered */ 175 ESC_TEST = 32, /* Enter in test mode */ 176 ESC_UTF8 = 64, 177 ESC_DCS =128, 178 }; 179 180 enum window_state { 181 WIN_VISIBLE = 1, 182 WIN_FOCUSED = 2 183 }; 184 185 enum selection_mode { 186 SEL_IDLE = 0, 187 SEL_EMPTY = 1, 188 SEL_READY = 2 189 }; 190 191 enum selection_type { 192 SEL_REGULAR = 1, 193 SEL_RECTANGULAR = 2 194 }; 195 196 enum selection_snap { 197 SNAP_WORD = 1, 198 SNAP_LINE = 2 199 }; 200 201 typedef unsigned char uchar; 202 typedef unsigned int uint; 203 typedef unsigned long ulong; 204 typedef unsigned short ushort; 205 206 typedef uint_least32_t Rune; 207 208 typedef struct { 209 Rune u; /* character code */ 210 ushort mode; /* attribute flags */ 211 uint32_t fg; /* foreground */ 212 uint32_t bg; /* background */ 213 } Glyph; 214 215 typedef Glyph *Line; 216 217 typedef struct { 218 Glyph attr; /* current char attributes */ 219 int x; 220 int y; 221 char state; 222 } TCursor; 223 224 /* CSI Escape sequence structs */ 225 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ 226 typedef struct { 227 char buf[ESC_BUF_SIZ]; /* raw string */ 228 int len; /* raw string length */ 229 char priv; 230 int arg[ESC_ARG_SIZ]; 231 int narg; /* nb of args */ 232 char mode[2]; 233 } CSIEscape; 234 235 /* STR Escape sequence structs */ 236 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */ 237 typedef struct { 238 char type; /* ESC type ... */ 239 char buf[STR_BUF_SIZ]; /* raw string */ 240 int len; /* raw string length */ 241 char *args[STR_ARG_SIZ]; 242 int narg; /* nb of args */ 243 } STREscape; 244 245 /* Internal representation of the screen */ 246 typedef struct { 247 int row; /* nb row */ 248 int col; /* nb col */ 249 Line *line; /* screen */ 250 Line *alt; /* alternate screen */ 251 int *dirty; /* dirtyness of lines */ 252 TCursor c; /* cursor */ 253 int top; /* top scroll limit */ 254 int bot; /* bottom scroll limit */ 255 int mode; /* terminal mode flags */ 256 int esc; /* escape state flags */ 257 char trantbl[4]; /* charset table translation */ 258 int charset; /* current charset */ 259 int icharset; /* selected charset for sequence */ 260 int numlock; /* lock numbers in keyboard */ 261 int *tabs; 262 } Term; 263 264 typedef struct { 265 struct xkb_context *ctx; 266 struct xkb_keymap *keymap; 267 struct xkb_state *state; 268 xkb_mod_index_t ctrl, alt, shift, logo; 269 unsigned int mods; 270 } XKB; 271 272 typedef struct { 273 struct wl_display *dpy; 274 struct wl_compositor *cmp; 275 struct wl_shm *shm; 276 struct wl_seat *seat; 277 struct wl_keyboard *keyboard; 278 struct wl_pointer *pointer; 279 struct wl_data_device_manager *datadevmanager; 280 struct wl_data_device *datadev; 281 struct wl_data_offer *seloffer; 282 struct wl_surface *surface; 283 struct zwp_text_input_v3 *textinput; 284 struct zwp_text_input_manager_v3 *timanager; 285 struct { 286 char commitbuf[4000]; 287 uint32_t delbefore; 288 uint32_t delafter; 289 } tistate; 290 struct { 291 char commitbuf[4000]; 292 uint32_t delbefore; 293 uint32_t delafter; 294 } tipending; 295 uint32_t textserial; 296 struct wl_buffer *buffer; 297 struct xdg_wm_base *wm; 298 struct xdg_surface *xdgsurface; 299 struct xdg_toplevel *toplevel; 300 XKB xkb; 301 bool configured; 302 int px, py; /* pointer x and y */ 303 int tw, th; /* tty width and height */ 304 int w, h; /* window width and height */ 305 int ch; /* char height */ 306 int cw; /* char width */ 307 int vis; 308 char state; /* focus, redraw, visible */ 309 int cursor; /* cursor style */ 310 struct wl_callback * framecb; 311 } Wayland; 312 313 typedef struct { 314 struct wld_context *ctx; 315 struct wld_font_context *fontctx; 316 struct wld_renderer *renderer; 317 struct wld_buffer *buffer, *oldbuffer; 318 } WLD; 319 320 typedef struct { 321 struct wl_cursor_theme *theme; 322 struct wl_cursor *cursor; 323 struct wl_surface *surface; 324 } Cursor; 325 326 typedef struct { 327 uint b; 328 uint mask; 329 char *s; 330 } MouseShortcut; 331 332 typedef struct { 333 int axis; 334 int dir; 335 uint mask; 336 char s[ESC_BUF_SIZ]; 337 } Axiskey; 338 339 typedef struct { 340 xkb_keysym_t k; 341 uint mask; 342 char *s; 343 /* three valued logic variables: 0 indifferent, 1 on, -1 off */ 344 signed char appkey; /* application keypad */ 345 signed char appcursor; /* application cursor */ 346 signed char crlf; /* crlf mode */ 347 } Key; 348 349 typedef struct { 350 int mode; 351 int type; 352 int snap; 353 /* 354 * Selection variables: 355 * nb – normalized coordinates of the beginning of the selection 356 * ne – normalized coordinates of the end of the selection 357 * ob – original coordinates of the beginning of the selection 358 * oe – original coordinates of the end of the selection 359 */ 360 struct { 361 int x, y; 362 } nb, ne, ob, oe; 363 364 char *primary; 365 struct wl_data_source *source; 366 int alt; 367 uint32_t tclick1, tclick2; 368 } Selection; 369 370 typedef union { 371 int i; 372 uint ui; 373 float f; 374 const void *v; 375 } Arg; 376 377 typedef struct { 378 uint mod; 379 xkb_keysym_t keysym; 380 void (*func)(const Arg *); 381 const Arg arg; 382 } Shortcut; 383 384 typedef struct { 385 char str[32]; 386 uint32_t key; 387 int len; 388 bool started; 389 struct timespec last; 390 } Repeat; 391 392 /* function definitions used in config.h */ 393 static void numlock(const Arg *); 394 static void selpaste(const Arg *); 395 static void wlzoom(const Arg *); 396 static void wlzoomabs(const Arg *); 397 static void wlzoomreset(const Arg *); 398 static void printsel(const Arg *); 399 static void printscreen(const Arg *) ; 400 static void iso14755(const Arg *); 401 static void toggleprinter(const Arg *); 402 static void sendbreak(const Arg *); 403 404 /* Config.h for applying patches and the configuration. */ 405 #include "config.h" 406 407 /* Font structure */ 408 typedef struct { 409 int height; 410 int width; 411 int ascent; 412 int descent; 413 int badslant; 414 int badweight; 415 short lbearing; 416 short rbearing; 417 struct wld_font *match; 418 FcFontSet *set; 419 FcPattern *pattern; 420 } Font; 421 422 /* Drawing Context */ 423 typedef struct { 424 uint32_t col[MAX(LEN(colorname), 256)]; 425 Font font, bfont, ifont, ibfont; 426 } DC; 427 428 static void die(const char *, ...); 429 static void draw(void); 430 static void redraw(void); 431 static void drawregion(int, int, int, int); 432 static void execsh(void); 433 static void stty(void); 434 static void sigchld(int); 435 static void run(void); 436 static void cresize(int, int); 437 438 static void csidump(void); 439 static void csihandle(void); 440 static void csiparse(void); 441 static void csireset(void); 442 static int eschandle(uchar); 443 static void strdump(void); 444 static void strhandle(void); 445 static void strparse(void); 446 static void strreset(void); 447 448 static int tattrset(int); 449 static void tprinter(char *, size_t); 450 static void tdumpsel(void); 451 static void tdumpline(int); 452 static void tdump(void); 453 static void tclearregion(int, int, int, int); 454 static void tcursor(int); 455 static void tdeletechar(int); 456 static void tdeleteline(int); 457 static void tinsertblank(int); 458 static void tinsertblankline(int); 459 static int tlinelen(int); 460 static void tmoveto(int, int); 461 static void tmoveato(int, int); 462 static void tnew(int, int); 463 static void tnewline(int); 464 static void tputtab(int); 465 static void tputc(Rune); 466 static void treset(void); 467 static void tresize(int, int); 468 static void tscrollup(int, int); 469 static void tscrolldown(int, int); 470 static void tsetattr(int *, int); 471 static void tsetchar(Rune, Glyph *, int, int); 472 static void tsetscroll(int, int); 473 static void tswapscreen(void); 474 static void tsetdirt(int, int); 475 static void tsetdirtattr(int); 476 static void tsetmode(int, int, int *, int); 477 static void tfulldirt(void); 478 static void techo(Rune); 479 static void tcontrolcode(uchar ); 480 static void tdectest(char ); 481 static void tdefutf8(char); 482 static int32_t tdefcolor(int *, int *, int); 483 static void tdeftran(char); 484 static inline int match(uint, uint); 485 static void ttynew(void); 486 static size_t ttyread(void); 487 static void ttyresize(void); 488 static void ttysend(char *, size_t); 489 static void ttywrite(const char *, size_t); 490 static void tstrsequence(uchar); 491 492 static inline uchar sixd_to_8bit(int); 493 static void wldraws(char *, Glyph, int, int, int, int); 494 static void wldrawglyph(Glyph, int, int); 495 static void wlclear(int, int, int, int); 496 static void wldrawcursor(void); 497 static void wlinit(void); 498 static void wlloadcols(void); 499 static int wlsetcolorname(int, const char *); 500 static void wlloadcursor(void); 501 static int wlloadfont(Font *, FcPattern *); 502 static void wlloadfonts(char *, double); 503 static void wlsettitle(char *); 504 static void wlresettitle(void); 505 static void wlseturgency(int); 506 static void wlsetsel(char*, uint32_t); 507 static void wlunloadfont(Font *f); 508 static void wlunloadfonts(void); 509 static void wlresize(int, int); 510 511 static void regglobal(void *, struct wl_registry *, uint32_t, const char *, 512 uint32_t); 513 static void regglobalremove(void *, struct wl_registry *, uint32_t); 514 static void surfenter(void *, struct wl_surface *, struct wl_output *); 515 static void surfleave(void *, struct wl_surface *, struct wl_output *); 516 static void framedone(void *, struct wl_callback *, uint32_t); 517 static void kbdkeymap(void *, struct wl_keyboard *, uint32_t, int32_t, uint32_t); 518 static void kbdenter(void *, struct wl_keyboard *, uint32_t, 519 struct wl_surface *, struct wl_array *); 520 static void kbdleave(void *, struct wl_keyboard *, uint32_t, 521 struct wl_surface *); 522 static void kbdkey(void *, struct wl_keyboard *, uint32_t, uint32_t, uint32_t, 523 uint32_t); 524 static void kbdmodifiers(void *, struct wl_keyboard *, uint32_t, uint32_t, 525 uint32_t, uint32_t, uint32_t); 526 static void kbdrepeatinfo(void *, struct wl_keyboard *, int32_t, int32_t); 527 static void ptrenter(void *, struct wl_pointer *, uint32_t, struct wl_surface *, 528 wl_fixed_t, wl_fixed_t); 529 static void ptrleave(void *, struct wl_pointer *, uint32_t, 530 struct wl_surface *); 531 static void ptrmotion(void *, struct wl_pointer *, uint32_t, 532 wl_fixed_t, wl_fixed_t); 533 static void ptrbutton(void *, struct wl_pointer *, uint32_t, uint32_t, 534 uint32_t, uint32_t); 535 static void ptraxis(void *, struct wl_pointer *, uint32_t, uint32_t, 536 wl_fixed_t); 537 static void tienter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface); 538 static void tileave(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface); 539 static void tipreedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end); 540 static void ticommit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text); 541 static void tidelete_surrounding_text(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t before_length, uint32_t after_length); 542 static void tidone(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t serial); 543 static void wmping(void *, struct xdg_wm_base *, uint32_t); 544 static void xdgsurfconfigure(void *, struct xdg_surface *, uint32_t); 545 static void toplevelconfigure(void *, struct xdg_toplevel *, 546 int32_t, int32_t, struct wl_array *); 547 static void toplevelclose(void *, struct xdg_toplevel *); 548 static void datadevoffer(void *, struct wl_data_device *, 549 struct wl_data_offer *); 550 static void datadeventer(void *, struct wl_data_device *, uint32_t, 551 struct wl_surface *, wl_fixed_t, wl_fixed_t, struct wl_data_offer *); 552 static void datadevleave(void *, struct wl_data_device *); 553 static void datadevmotion(void *, struct wl_data_device *, uint32_t, 554 wl_fixed_t x, wl_fixed_t y); 555 static void datadevdrop(void *, struct wl_data_device *); 556 static void datadevselection(void *, struct wl_data_device *, 557 struct wl_data_offer *); 558 static void dataofferoffer(void *, struct wl_data_offer *, const char *); 559 static void datasrctarget(void *, struct wl_data_source *, const char *); 560 static void datasrcsend(void *, struct wl_data_source *, const char *, int32_t); 561 static void datasrccancelled(void *, struct wl_data_source *); 562 563 static void selinit(void); 564 static void selnormalize(void); 565 static inline int selected(int, int); 566 static char *getsel(void); 567 static void selcopy(uint32_t); 568 static void selscroll(int, int); 569 static void selsnap(int *, int *, int); 570 static int x2col(int); 571 static int y2row(int); 572 573 static size_t utf8decode(char *, Rune *, size_t); 574 static Rune utf8decodebyte(char, size_t *); 575 static size_t utf8encode(Rune, char *); 576 static char utf8encodebyte(Rune, size_t); 577 static char *utf8strchr(char *s, Rune u); 578 static size_t utf8validate(Rune *, size_t); 579 580 static ssize_t xwrite(int, const char *, size_t); 581 static void *xmalloc(size_t); 582 static void *xrealloc(void *, size_t); 583 static char *xstrdup(char *); 584 585 static int subprocwd(char *, size_t); 586 587 static void usage(void); 588 589 static struct wl_registry_listener reglistener = { regglobal, regglobalremove }; 590 static struct wl_surface_listener surflistener = { surfenter, surfleave }; 591 static struct wl_callback_listener framelistener = { framedone }; 592 static struct wl_keyboard_listener kbdlistener = 593 { kbdkeymap, kbdenter, kbdleave, kbdkey, kbdmodifiers, kbdrepeatinfo }; 594 static struct wl_pointer_listener ptrlistener = 595 { ptrenter, ptrleave, ptrmotion, ptrbutton, ptraxis }; 596 static struct xdg_wm_base_listener wmlistener = { wmping }; 597 static struct xdg_surface_listener xdgsurflistener = { xdgsurfconfigure }; 598 static struct xdg_toplevel_listener toplevellistener = 599 { toplevelconfigure, toplevelclose }; 600 static struct wl_data_device_listener datadevlistener = 601 { datadevoffer, datadeventer, datadevleave, datadevmotion, datadevdrop, 602 datadevselection }; 603 static struct wl_data_offer_listener dataofferlistener = { dataofferoffer }; 604 static struct wl_data_source_listener datasrclistener = 605 { datasrctarget, datasrcsend, datasrccancelled }; 606 static struct zwp_text_input_v3_listener tilistener = { 607 .enter = tienter, 608 .leave = tileave, 609 .preedit_string = tipreedit_string, 610 .commit_string = ticommit_string, 611 .delete_surrounding_text = tidelete_surrounding_text, 612 .done = tidone 613 }; 614 615 /* Globals */ 616 static int plumbsel; 617 static DC dc; 618 static Wayland wl; 619 static WLD wld; 620 static Cursor cursor; 621 static Term term; 622 static CSIEscape csiescseq; 623 static STREscape strescseq; 624 static int cmdfd; 625 static pid_t pid; 626 static Selection sel; 627 static Repeat repeat; 628 static bool needdraw = true; 629 static int iofd = 1; 630 static char **opt_cmd = NULL; 631 static char *opt_class = NULL; 632 static char *opt_embed = NULL; 633 static char *opt_font = NULL; 634 static char *opt_io = NULL; 635 static char *opt_line = NULL; 636 static char *opt_name = NULL; 637 static char *opt_title = NULL; 638 static int oldbutton = 3; /* button event on startup: 3 = release */ 639 static int oldx, oldy; 640 641 static char *usedfont = NULL; 642 static double usedfontsize = 0; 643 static double defaultfontsize = 0; 644 645 static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 646 static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 647 static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 648 static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 649 650 /* Font Ring Cache */ 651 enum { 652 FRC_NORMAL, 653 FRC_ITALIC, 654 FRC_BOLD, 655 FRC_ITALICBOLD 656 }; 657 658 typedef struct { 659 struct wld_font *font; 660 int flags; 661 Rune unicodep; 662 } Fontcache; 663 664 /* Fontcache is an array now. A new font will be appended to the array. */ 665 static Fontcache frc[16]; 666 static int frclen = 0; 667 668 ssize_t 669 xwrite(int fd, const char *s, size_t len) 670 { 671 size_t aux = len; 672 ssize_t r; 673 674 while (len > 0) { 675 r = write(fd, s, len); 676 if (r < 0) 677 return r; 678 len -= r; 679 s += r; 680 } 681 682 return aux; 683 } 684 685 void * 686 xmalloc(size_t len) 687 { 688 void *p = malloc(len); 689 690 if (!p) 691 die("Out of memory\n"); 692 693 return p; 694 } 695 696 void * 697 xrealloc(void *p, size_t len) 698 { 699 if ((p = realloc(p, len)) == NULL) 700 die("Out of memory\n"); 701 702 return p; 703 } 704 705 char * 706 xstrdup(char *s) 707 { 708 if ((s = strdup(s)) == NULL) 709 die("Out of memory\n"); 710 711 return s; 712 } 713 714 size_t 715 utf8decode(char *c, Rune *u, size_t clen) 716 { 717 size_t i, j, len, type; 718 Rune udecoded; 719 720 *u = UTF_INVALID; 721 if (!clen) 722 return 0; 723 udecoded = utf8decodebyte(c[0], &len); 724 if (!BETWEEN(len, 1, UTF_SIZ)) 725 return 1; 726 for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 727 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 728 if (type != 0) 729 return j; 730 } 731 if (j < len) 732 return 0; 733 *u = udecoded; 734 utf8validate(u, len); 735 736 return len; 737 } 738 739 Rune 740 utf8decodebyte(char c, size_t *i) 741 { 742 for (*i = 0; *i < LEN(utfmask); ++(*i)) 743 if (((uchar)c & utfmask[*i]) == utfbyte[*i]) 744 return (uchar)c & ~utfmask[*i]; 745 746 return 0; 747 } 748 749 size_t 750 utf8encode(Rune u, char *c) 751 { 752 size_t len, i; 753 754 len = utf8validate(&u, 0); 755 if (len > UTF_SIZ) 756 return 0; 757 758 for (i = len - 1; i != 0; --i) { 759 c[i] = utf8encodebyte(u, 0); 760 u >>= 6; 761 } 762 c[0] = utf8encodebyte(u, len); 763 764 return len; 765 } 766 767 char 768 utf8encodebyte(Rune u, size_t i) 769 { 770 return utfbyte[i] | (u & ~utfmask[i]); 771 } 772 773 char * 774 utf8strchr(char *s, Rune u) 775 { 776 Rune r; 777 size_t i, j, len; 778 779 len = strlen(s); 780 for (i = 0, j = 0; i < len; i += j) { 781 if (!(j = utf8decode(&s[i], &r, len - i))) 782 break; 783 if (r == u) 784 return &(s[i]); 785 } 786 787 return NULL; 788 } 789 790 size_t 791 utf8validate(Rune *u, size_t i) 792 { 793 if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 794 *u = UTF_INVALID; 795 for (i = 1; *u > utfmax[i]; ++i) 796 ; 797 798 return i; 799 } 800 801 int 802 subprocwd(char *path, size_t len) 803 { 804 #if defined(__linux__) 805 if (snprintf(path, len, "/proc/%d/cwd", pid) < 0) 806 return -1; 807 return 0; 808 #elif defined(__OpenBSD__) 809 int name[3] = {CTL_KERN, KERN_PROC_CWD, pid}; 810 if (sysctl(name, 3, path, &len, 0, 0) == -1) 811 return -1; 812 return 0; 813 #endif 814 } 815 816 void 817 selinit(void) 818 { 819 sel.tclick1 = 0; 820 sel.tclick2 = 0; 821 sel.mode = SEL_IDLE; 822 sel.snap = 0; 823 sel.ob.x = -1; 824 sel.primary = NULL; 825 sel.source = NULL; 826 } 827 828 int 829 x2col(int x) 830 { 831 x -= borderpx; 832 x /= wl.cw; 833 834 return LIMIT(x, 0, term.col-1); 835 } 836 837 int 838 y2row(int y) 839 { 840 y -= borderpx; 841 y /= wl.ch; 842 843 return LIMIT(y, 0, term.row-1); 844 } 845 846 int 847 tlinelen(int y) 848 { 849 int i = term.col; 850 851 if (term.line[y][i - 1].mode & ATTR_WRAP) 852 return i; 853 854 while (i > 0 && term.line[y][i - 1].u == ' ') 855 --i; 856 857 return i; 858 } 859 860 void 861 selnormalize(void) 862 { 863 int i; 864 865 if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { 866 sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; 867 sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; 868 } else { 869 sel.nb.x = MIN(sel.ob.x, sel.oe.x); 870 sel.ne.x = MAX(sel.ob.x, sel.oe.x); 871 } 872 sel.nb.y = MIN(sel.ob.y, sel.oe.y); 873 sel.ne.y = MAX(sel.ob.y, sel.oe.y); 874 875 selsnap(&sel.nb.x, &sel.nb.y, -1); 876 selsnap(&sel.ne.x, &sel.ne.y, +1); 877 878 /* expand selection over line breaks */ 879 if (sel.type == SEL_RECTANGULAR) 880 return; 881 i = tlinelen(sel.nb.y); 882 if (i < sel.nb.x) 883 sel.nb.x = i; 884 if (tlinelen(sel.ne.y) <= sel.ne.x) 885 sel.ne.x = term.col - 1; 886 } 887 888 int 889 selected(int x, int y) 890 { 891 if (sel.mode == SEL_EMPTY) 892 return 0; 893 894 if (sel.type == SEL_RECTANGULAR) 895 return BETWEEN(y, sel.nb.y, sel.ne.y) 896 && BETWEEN(x, sel.nb.x, sel.ne.x); 897 898 return BETWEEN(y, sel.nb.y, sel.ne.y) 899 && (y != sel.nb.y || x >= sel.nb.x) 900 && (y != sel.ne.y || x <= sel.ne.x); 901 } 902 903 void 904 selsnap(int *x, int *y, int direction) 905 { 906 int newx, newy, xt, yt; 907 int delim, prevdelim; 908 Glyph *gp, *prevgp; 909 910 switch (sel.snap) { 911 case SNAP_WORD: 912 /* 913 * Snap around if the word wraps around at the end or 914 * beginning of a line. 915 */ 916 prevgp = &term.line[*y][*x]; 917 prevdelim = ISDELIM(prevgp->u); 918 for (;;) { 919 newx = *x + direction; 920 newy = *y; 921 if (!BETWEEN(newx, 0, term.col - 1)) { 922 newy += direction; 923 newx = (newx + term.col) % term.col; 924 if (!BETWEEN(newy, 0, term.row - 1)) 925 break; 926 927 if (direction > 0) 928 yt = *y, xt = *x; 929 else 930 yt = newy, xt = newx; 931 if (!(term.line[yt][xt].mode & ATTR_WRAP)) 932 break; 933 } 934 935 if (newx >= tlinelen(newy)) 936 break; 937 938 gp = &term.line[newy][newx]; 939 delim = ISDELIM(gp->u); 940 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim 941 || (delim && gp->u != prevgp->u))) 942 break; 943 944 *x = newx; 945 *y = newy; 946 prevgp = gp; 947 prevdelim = delim; 948 } 949 break; 950 case SNAP_LINE: 951 /* 952 * Snap around if the the previous line or the current one 953 * has set ATTR_WRAP at its end. Then the whole next or 954 * previous line will be selected. 955 */ 956 *x = (direction < 0) ? 0 : term.col - 1; 957 if (direction < 0) { 958 for (; *y > 0; *y += direction) { 959 if (!(term.line[*y-1][term.col-1].mode 960 & ATTR_WRAP)) { 961 break; 962 } 963 } 964 } else if (direction > 0) { 965 for (; *y < term.row-1; *y += direction) { 966 if (!(term.line[*y][term.col-1].mode 967 & ATTR_WRAP)) { 968 break; 969 } 970 } 971 } 972 break; 973 } 974 } 975 976 void 977 getbuttoninfo(void) 978 { 979 int type; 980 uint state = wl.xkb.mods & ~forceselmod; 981 982 sel.alt = IS_SET(MODE_ALTSCREEN); 983 984 sel.oe.x = x2col(wl.px); 985 sel.oe.y = y2row(wl.py); 986 selnormalize(); 987 988 sel.type = SEL_REGULAR; 989 for (type = 1; type < LEN(selmasks); ++type) { 990 if (match(selmasks[type], state)) { 991 sel.type = type; 992 break; 993 } 994 } 995 } 996 997 void 998 wlmousereport(int button, bool release, int x, int y) 999 { 1000 int len; 1001 char buf[40]; 1002 1003 if (!IS_SET(MODE_MOUSEX10)) { 1004 button += ((wl.xkb.mods & MOD_MASK_SHIFT) ? 4 : 0) 1005 + ((wl.xkb.mods & MOD_MASK_LOGO ) ? 8 : 0) 1006 + ((wl.xkb.mods & MOD_MASK_CTRL ) ? 16 : 0); 1007 } 1008 1009 if (IS_SET(MODE_MOUSESGR)) { 1010 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", 1011 button, x+1, y+1, release ? 'm' : 'M'); 1012 } else if (x < 223 && y < 223) { 1013 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 1014 32+button, 32+x+1, 32+y+1); 1015 } else { 1016 return; 1017 } 1018 1019 ttywrite(buf, len); 1020 } 1021 1022 void 1023 wlmousereportbutton(uint32_t button, uint32_t state) 1024 { 1025 bool release = state == WL_POINTER_BUTTON_STATE_RELEASED; 1026 1027 if (!IS_SET(MODE_MOUSESGR) && release) { 1028 button = 3; 1029 } else { 1030 switch (button) { 1031 case BTN_LEFT: 1032 button = 0; 1033 break; 1034 case BTN_MIDDLE: 1035 button = 1; 1036 break; 1037 case BTN_RIGHT: 1038 button = 2; 1039 break; 1040 } 1041 } 1042 1043 oldbutton = release ? 3 : button; 1044 1045 /* don't report release events when in X10 mode */ 1046 if (IS_SET(MODE_MOUSEX10) && release) { 1047 return; 1048 } 1049 1050 wlmousereport(button, release, oldx, oldy); 1051 } 1052 1053 void 1054 wlmousereportmotion(wl_fixed_t fx, wl_fixed_t fy) 1055 { 1056 int x = x2col(wl_fixed_to_int(fx)), y = y2row(wl_fixed_to_int(fy)); 1057 1058 if (x == oldx && y == oldy) 1059 return; 1060 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) 1061 return; 1062 /* MOUSE_MOTION: no reporting if no button is pressed */ 1063 if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) 1064 return; 1065 1066 oldx = x; 1067 oldy = y; 1068 wlmousereport(oldbutton + 32, false, x, y); 1069 } 1070 1071 void 1072 wlmousereportaxis(uint32_t axis, wl_fixed_t amount) 1073 { 1074 wlmousereport(64 + (axis == AXIS_VERTICAL ? 4 : 6) 1075 + (amount > 0 ? 1 : 0), false, oldx, oldy); 1076 } 1077 1078 char * 1079 getsel(void) 1080 { 1081 char *str, *ptr; 1082 int y, bufsize, lastx, linelen; 1083 Glyph *gp, *last; 1084 1085 if (sel.ob.x == -1) 1086 return NULL; 1087 1088 bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; 1089 ptr = str = xmalloc(bufsize); 1090 1091 /* append every set & selected glyph to the selection */ 1092 for (y = sel.nb.y; y <= sel.ne.y; y++) { 1093 if ((linelen = tlinelen(y)) == 0) { 1094 *ptr++ = '\n'; 1095 continue; 1096 } 1097 1098 if (sel.type == SEL_RECTANGULAR) { 1099 gp = &term.line[y][sel.nb.x]; 1100 lastx = sel.ne.x; 1101 } else { 1102 gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; 1103 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; 1104 } 1105 last = &term.line[y][MIN(lastx, linelen-1)]; 1106 while (last >= gp && last->u == ' ') 1107 --last; 1108 1109 for ( ; gp <= last; ++gp) { 1110 if (gp->mode & ATTR_WDUMMY) 1111 continue; 1112 1113 ptr += utf8encode(gp->u, ptr); 1114 } 1115 1116 /* 1117 * Copy and pasting of line endings is inconsistent 1118 * in the inconsistent terminal and GUI world. 1119 * The best solution seems like to produce '\n' when 1120 * something is copied from st and convert '\n' to 1121 * '\r', when something to be pasted is received by 1122 * st. 1123 * FIXME: Fix the computer world. 1124 */ 1125 if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) 1126 *ptr++ = '\n'; 1127 } 1128 *ptr = 0; 1129 return str; 1130 } 1131 1132 void 1133 selcopy(uint32_t serial) 1134 { 1135 wlsetsel(getsel(), serial); 1136 } 1137 1138 static inline void 1139 selwritebuf(char *buf, int len) 1140 { 1141 char *repl = buf; 1142 1143 /* 1144 * As seen in getsel: 1145 * Line endings are inconsistent in the terminal and GUI world 1146 * copy and pasting. When receiving some selection data, 1147 * replace all '\n' with '\r'. 1148 * FIXME: Fix the computer world. 1149 */ 1150 while ((repl = memchr(repl, '\n', len))) { 1151 *repl++ = '\r'; 1152 } 1153 1154 ttysend(buf, len); 1155 } 1156 1157 void 1158 selpaste(const Arg *dummy) 1159 { 1160 int fds[2], len, left; 1161 char buf[BUFSIZ], *str; 1162 1163 if (wl.seloffer) { 1164 if (IS_SET(MODE_BRCKTPASTE)) 1165 ttywrite("\033[200~", 6); 1166 /* check if we are pasting from ourselves */ 1167 if (sel.source) { 1168 str = sel.primary; 1169 left = strlen(sel.primary); 1170 while (left > 0) { 1171 len = MIN(sizeof buf, left); 1172 memcpy(buf, str, len); 1173 selwritebuf(buf, len); 1174 left -= len; 1175 str += len; 1176 } 1177 } else { 1178 pipe(fds); 1179 wl_data_offer_receive(wl.seloffer, "text/plain", fds[1]); 1180 wl_display_flush(wl.dpy); 1181 close(fds[1]); 1182 while ((len = read(fds[0], buf, sizeof buf)) > 0) { 1183 selwritebuf(buf, len); 1184 } 1185 close(fds[0]); 1186 } 1187 if (IS_SET(MODE_BRCKTPASTE)) 1188 ttywrite("\033[201~", 6); 1189 } 1190 } 1191 1192 void 1193 selclear(void) 1194 { 1195 if (sel.ob.x == -1) 1196 return; 1197 sel.mode = SEL_IDLE; 1198 sel.ob.x = -1; 1199 tsetdirt(sel.nb.y, sel.ne.y); 1200 } 1201 1202 void 1203 wlsetsel(char *str, uint32_t serial) 1204 { 1205 free(sel.primary); 1206 sel.primary = str; 1207 1208 if (str) { 1209 sel.source = wl_data_device_manager_create_data_source(wl.datadevmanager); 1210 wl_data_source_add_listener(sel.source, &datasrclistener, NULL); 1211 wl_data_source_offer(sel.source, "text/plain; charset=utf-8"); 1212 } else { 1213 sel.source = NULL; 1214 } 1215 wl_data_device_set_selection(wl.datadev, sel.source, serial); 1216 } 1217 1218 void 1219 plumbinit(void) 1220 { 1221 for (plumbsel = 0; plumb_cmd[plumbsel]; ++plumbsel) 1222 ; 1223 } 1224 1225 void 1226 plumb(char *sel) 1227 { 1228 char cwd[PATH_MAX]; 1229 1230 if (!sel || subprocwd(cwd, sizeof(cwd)) != 0) 1231 return; 1232 plumb_cmd[plumbsel] = sel; 1233 1234 if (fork() == 0) { 1235 if (chdir(cwd) == 0) 1236 execvp(plumb_cmd[0], plumb_cmd); 1237 _exit(1); 1238 } 1239 } 1240 1241 void 1242 die(const char *errstr, ...) 1243 { 1244 va_list ap; 1245 1246 va_start(ap, errstr); 1247 vfprintf(stderr, errstr, ap); 1248 va_end(ap); 1249 exit(1); 1250 } 1251 1252 void 1253 execsh(void) 1254 { 1255 char **args, *sh, *prog; 1256 const struct passwd *pw; 1257 1258 errno = 0; 1259 if ((pw = getpwuid(getuid())) == NULL) { 1260 if (errno) 1261 die("getpwuid:%s\n", strerror(errno)); 1262 else 1263 die("who are you?\n"); 1264 } 1265 1266 if ((sh = getenv("SHELL")) == NULL) 1267 sh = (pw->pw_shell[0]) ? pw->pw_shell : shell; 1268 1269 if (opt_cmd) 1270 prog = opt_cmd[0]; 1271 else if (utmp) 1272 prog = utmp; 1273 else 1274 prog = sh; 1275 args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL}; 1276 1277 unsetenv("COLUMNS"); 1278 unsetenv("LINES"); 1279 unsetenv("TERMCAP"); 1280 setenv("LOGNAME", pw->pw_name, 1); 1281 setenv("USER", pw->pw_name, 1); 1282 setenv("SHELL", sh, 1); 1283 setenv("HOME", pw->pw_dir, 1); 1284 setenv("TERM", termname, 1); 1285 1286 signal(SIGCHLD, SIG_DFL); 1287 signal(SIGHUP, SIG_DFL); 1288 signal(SIGINT, SIG_DFL); 1289 signal(SIGQUIT, SIG_DFL); 1290 signal(SIGTERM, SIG_DFL); 1291 signal(SIGALRM, SIG_DFL); 1292 1293 execvp(prog, args); 1294 _exit(1); 1295 } 1296 1297 void 1298 sigchld(int a) 1299 { 1300 int stat; 1301 pid_t p; 1302 1303 for (;;) { 1304 p = waitpid(-1, &stat, WNOHANG); 1305 if (p == 0) 1306 break; 1307 if (p < 0) 1308 die("waitpid: %s\n", strerror(errno)); 1309 if (pid == p) { 1310 if (!WIFEXITED(stat) || WEXITSTATUS(stat)) 1311 die("child finished with error '%d'\n", stat); 1312 exit(0); 1313 } 1314 } 1315 } 1316 1317 1318 void 1319 stty(void) 1320 { 1321 char cmd[_POSIX_ARG_MAX], **p, *q, *s; 1322 size_t n, siz; 1323 1324 if ((n = strlen(stty_args)) > sizeof(cmd)-1) 1325 die("incorrect stty parameters\n"); 1326 memcpy(cmd, stty_args, n); 1327 q = cmd + n; 1328 siz = sizeof(cmd) - n; 1329 for (p = opt_cmd; p && (s = *p); ++p) { 1330 if ((n = strlen(s)) > siz-1) 1331 die("stty parameter length too long\n"); 1332 *q++ = ' '; 1333 memcpy(q, s, n); 1334 q += n; 1335 siz -= n + 1; 1336 } 1337 *q = '\0'; 1338 if (system(cmd) != 0) 1339 perror("Couldn't call stty"); 1340 } 1341 1342 void 1343 ttynew(void) 1344 { 1345 int m, s; 1346 struct winsize w = {term.row, term.col, 0, 0}; 1347 1348 if (opt_io) { 1349 term.mode |= MODE_PRINT; 1350 iofd = (!strcmp(opt_io, "-")) ? 1351 1 : open(opt_io, O_WRONLY | O_CREAT, 0666); 1352 if (iofd < 0) { 1353 fprintf(stderr, "Error opening %s:%s\n", 1354 opt_io, strerror(errno)); 1355 } 1356 } 1357 1358 if (opt_line) { 1359 if ((cmdfd = open(opt_line, O_RDWR)) < 0) 1360 die("open line failed: %s\n", strerror(errno)); 1361 dup2(cmdfd, 0); 1362 stty(); 1363 return; 1364 } 1365 1366 /* seems to work fine on linux, openbsd and freebsd */ 1367 if (openpty(&m, &s, NULL, NULL, &w) < 0) 1368 die("openpty failed: %s\n", strerror(errno)); 1369 1370 switch (pid = fork()) { 1371 case -1: 1372 die("fork failed\n"); 1373 break; 1374 case 0: 1375 close(iofd); 1376 setsid(); /* create a new process group */ 1377 dup2(s, 0); 1378 dup2(s, 1); 1379 dup2(s, 2); 1380 if (ioctl(s, TIOCSCTTY, NULL) < 0) 1381 die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); 1382 close(s); 1383 close(m); 1384 execsh(); 1385 break; 1386 default: 1387 close(s); 1388 cmdfd = m; 1389 signal(SIGCHLD, sigchld); 1390 break; 1391 } 1392 } 1393 1394 size_t 1395 ttyread(void) 1396 { 1397 static char buf[BUFSIZ]; 1398 static int buflen = 0; 1399 char *ptr; 1400 int charsize; /* size of utf8 char in bytes */ 1401 Rune unicodep; 1402 int ret; 1403 1404 /* append read bytes to unprocessed bytes */ 1405 if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) 1406 die("Couldn't read from shell: %s\n", strerror(errno)); 1407 1408 buflen += ret; 1409 ptr = buf; 1410 1411 for (;;) { 1412 if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 1413 /* process a complete utf8 char */ 1414 charsize = utf8decode(ptr, &unicodep, buflen); 1415 if (charsize == 0) 1416 break; 1417 tputc(unicodep); 1418 ptr += charsize; 1419 buflen -= charsize; 1420 1421 } else { 1422 if (buflen <= 0) 1423 break; 1424 tputc(*ptr++ & 0xFF); 1425 buflen--; 1426 } 1427 } 1428 /* keep any uncomplete utf8 char for the next call */ 1429 if (buflen > 0) 1430 memmove(buf, ptr, buflen); 1431 1432 needdraw = true; 1433 return ret; 1434 } 1435 1436 void 1437 ttywrite(const char *s, size_t n) 1438 { 1439 fd_set wfd, rfd; 1440 ssize_t r; 1441 size_t lim = 256; 1442 1443 /* 1444 * Remember that we are using a pty, which might be a modem line. 1445 * Writing too much will clog the line. That's why we are doing this 1446 * dance. 1447 * FIXME: Migrate the world to Plan 9. 1448 */ 1449 while (n > 0) { 1450 FD_ZERO(&wfd); 1451 FD_ZERO(&rfd); 1452 FD_SET(cmdfd, &wfd); 1453 FD_SET(cmdfd, &rfd); 1454 1455 /* Check if we can write. */ 1456 if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { 1457 if (errno == EINTR) 1458 continue; 1459 die("select failed: %s\n", strerror(errno)); 1460 } 1461 if (FD_ISSET(cmdfd, &wfd)) { 1462 /* 1463 * Only write the bytes written by ttywrite() or the 1464 * default of 256. This seems to be a reasonable value 1465 * for a serial line. Bigger values might clog the I/O. 1466 */ 1467 if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) 1468 goto write_error; 1469 if (r < n) { 1470 /* 1471 * We weren't able to write out everything. 1472 * This means the buffer is getting full 1473 * again. Empty it. 1474 */ 1475 if (n < lim) 1476 lim = ttyread(); 1477 n -= r; 1478 s += r; 1479 } else { 1480 /* All bytes have been written. */ 1481 break; 1482 } 1483 } 1484 if (FD_ISSET(cmdfd, &rfd)) 1485 lim = ttyread(); 1486 } 1487 return; 1488 1489 write_error: 1490 die("write error on tty: %s\n", strerror(errno)); 1491 } 1492 1493 void 1494 ttysend(char *s, size_t n) 1495 { 1496 int len; 1497 char *t, *lim; 1498 Rune u; 1499 1500 ttywrite(s, n); 1501 if (!IS_SET(MODE_ECHO)) 1502 return; 1503 1504 lim = &s[n]; 1505 for (t = s; t < lim; t += len) { 1506 if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 1507 len = utf8decode(t, &u, n); 1508 } else { 1509 u = *t & 0xFF; 1510 len = 1; 1511 } 1512 if (len <= 0) 1513 break; 1514 techo(u); 1515 n -= len; 1516 } 1517 } 1518 1519 void 1520 ttyresize(void) 1521 { 1522 struct winsize w; 1523 1524 w.ws_row = term.row; 1525 w.ws_col = term.col; 1526 w.ws_xpixel = wl.tw; 1527 w.ws_ypixel = wl.th; 1528 if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) 1529 fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); 1530 } 1531 1532 int 1533 tattrset(int attr) 1534 { 1535 int i, j; 1536 1537 for (i = 0; i < term.row-1; i++) { 1538 for (j = 0; j < term.col-1; j++) { 1539 if (term.line[i][j].mode & attr) 1540 return 1; 1541 } 1542 } 1543 1544 return 0; 1545 } 1546 1547 void 1548 tsetdirt(int top, int bot) 1549 { 1550 int i; 1551 1552 LIMIT(top, 0, term.row-1); 1553 LIMIT(bot, 0, term.row-1); 1554 1555 for (i = top; i <= bot; i++) 1556 term.dirty[i] = 1; 1557 1558 needdraw = true; 1559 } 1560 1561 void 1562 tsetdirtattr(int attr) 1563 { 1564 int i, j; 1565 1566 for (i = 0; i < term.row-1; i++) { 1567 for (j = 0; j < term.col-1; j++) { 1568 if (term.line[i][j].mode & attr) { 1569 tsetdirt(i, i); 1570 break; 1571 } 1572 } 1573 } 1574 } 1575 1576 void 1577 tfulldirt(void) 1578 { 1579 tsetdirt(0, term.row-1); 1580 } 1581 1582 void 1583 tcursor(int mode) 1584 { 1585 static TCursor c[2]; 1586 int alt = IS_SET(MODE_ALTSCREEN); 1587 1588 if (mode == CURSOR_SAVE) { 1589 c[alt] = term.c; 1590 } else if (mode == CURSOR_LOAD) { 1591 term.c = c[alt]; 1592 tmoveto(c[alt].x, c[alt].y); 1593 } 1594 } 1595 1596 void 1597 treset(void) 1598 { 1599 uint i; 1600 1601 term.c = (TCursor){{ 1602 .mode = ATTR_NULL, 1603 .fg = defaultfg, 1604 .bg = defaultbg 1605 }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; 1606 1607 memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1608 for (i = tabspaces; i < term.col; i += tabspaces) 1609 term.tabs[i] = 1; 1610 term.top = 0; 1611 term.bot = term.row - 1; 1612 term.mode = MODE_WRAP|MODE_UTF8; 1613 memset(term.trantbl, CS_USA, sizeof(term.trantbl)); 1614 term.charset = 0; 1615 1616 for (i = 0; i < 2; i++) { 1617 tmoveto(0, 0); 1618 tcursor(CURSOR_SAVE); 1619 tclearregion(0, 0, term.col-1, term.row-1); 1620 tswapscreen(); 1621 } 1622 } 1623 1624 void 1625 tnew(int col, int row) 1626 { 1627 term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; 1628 tresize(col, row); 1629 term.numlock = 1; 1630 1631 treset(); 1632 } 1633 1634 void 1635 tswapscreen(void) 1636 { 1637 Line *tmp = term.line; 1638 1639 term.line = term.alt; 1640 term.alt = tmp; 1641 term.mode ^= MODE_ALTSCREEN; 1642 tfulldirt(); 1643 } 1644 1645 void 1646 tscrolldown(int orig, int n) 1647 { 1648 int i; 1649 Line temp; 1650 1651 LIMIT(n, 0, term.bot-orig+1); 1652 1653 tsetdirt(orig, term.bot-n); 1654 tclearregion(0, term.bot-n+1, term.col-1, term.bot); 1655 1656 for (i = term.bot; i >= orig+n; i--) { 1657 temp = term.line[i]; 1658 term.line[i] = term.line[i-n]; 1659 term.line[i-n] = temp; 1660 } 1661 1662 selscroll(orig, n); 1663 } 1664 1665 void 1666 tscrollup(int orig, int n) 1667 { 1668 int i; 1669 Line temp; 1670 1671 LIMIT(n, 0, term.bot-orig+1); 1672 1673 tclearregion(0, orig, term.col-1, orig+n-1); 1674 tsetdirt(orig+n, term.bot); 1675 1676 for (i = orig; i <= term.bot-n; i++) { 1677 temp = term.line[i]; 1678 term.line[i] = term.line[i+n]; 1679 term.line[i+n] = temp; 1680 } 1681 1682 selscroll(orig, -n); 1683 } 1684 1685 void 1686 selscroll(int orig, int n) 1687 { 1688 if (sel.ob.x == -1) 1689 return; 1690 1691 if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { 1692 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { 1693 selclear(); 1694 return; 1695 } 1696 if (sel.type == SEL_RECTANGULAR) { 1697 if (sel.ob.y < term.top) 1698 sel.ob.y = term.top; 1699 if (sel.oe.y > term.bot) 1700 sel.oe.y = term.bot; 1701 } else { 1702 if (sel.ob.y < term.top) { 1703 sel.ob.y = term.top; 1704 sel.ob.x = 0; 1705 } 1706 if (sel.oe.y > term.bot) { 1707 sel.oe.y = term.bot; 1708 sel.oe.x = term.col; 1709 } 1710 } 1711 selnormalize(); 1712 } 1713 } 1714 1715 void 1716 tnewline(int first_col) 1717 { 1718 int y = term.c.y; 1719 1720 if (y == term.bot) { 1721 tscrollup(term.top, 1); 1722 } else { 1723 y++; 1724 } 1725 tmoveto(first_col ? 0 : term.c.x, y); 1726 } 1727 1728 void 1729 csiparse(void) 1730 { 1731 char *p = csiescseq.buf, *np; 1732 long int v; 1733 1734 csiescseq.narg = 0; 1735 if (*p == '?') { 1736 csiescseq.priv = 1; 1737 p++; 1738 } 1739 1740 csiescseq.buf[csiescseq.len] = '\0'; 1741 while (p < csiescseq.buf+csiescseq.len) { 1742 np = NULL; 1743 v = strtol(p, &np, 10); 1744 if (np == p) 1745 v = 0; 1746 if (v == LONG_MAX || v == LONG_MIN) 1747 v = -1; 1748 csiescseq.arg[csiescseq.narg++] = v; 1749 p = np; 1750 if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) 1751 break; 1752 p++; 1753 } 1754 csiescseq.mode[0] = *p++; 1755 csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; 1756 } 1757 1758 /* for absolute user moves, when decom is set */ 1759 void 1760 tmoveato(int x, int y) 1761 { 1762 tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); 1763 } 1764 1765 void 1766 tmoveto(int x, int y) 1767 { 1768 int miny, maxy; 1769 1770 if (term.c.state & CURSOR_ORIGIN) { 1771 miny = term.top; 1772 maxy = term.bot; 1773 } else { 1774 miny = 0; 1775 maxy = term.row - 1; 1776 } 1777 term.c.state &= ~CURSOR_WRAPNEXT; 1778 term.c.x = LIMIT(x, 0, term.col-1); 1779 term.c.y = LIMIT(y, miny, maxy); 1780 } 1781 1782 void 1783 tsetchar(Rune u, Glyph *attr, int x, int y) 1784 { 1785 static char *vt100_0[62] = { /* 0x41 - 0x7e */ 1786 "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 1787 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 1788 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ 1789 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ 1790 "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ 1791 "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ 1792 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 1793 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 1794 }; 1795 1796 /* 1797 * The table is proudly stolen from rxvt. 1798 */ 1799 if (term.trantbl[term.charset] == CS_GRAPHIC0 && 1800 BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) 1801 utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); 1802 1803 if (term.line[y][x].mode & ATTR_WIDE) { 1804 if (x+1 < term.col) { 1805 term.line[y][x+1].u = ' '; 1806 term.line[y][x+1].mode &= ~ATTR_WDUMMY; 1807 } 1808 } else if (term.line[y][x].mode & ATTR_WDUMMY) { 1809 term.line[y][x-1].u = ' '; 1810 term.line[y][x-1].mode &= ~ATTR_WIDE; 1811 } 1812 1813 term.dirty[y] = 1; 1814 term.line[y][x] = *attr; 1815 term.line[y][x].u = u; 1816 } 1817 1818 void 1819 tclearregion(int x1, int y1, int x2, int y2) 1820 { 1821 int x, y, temp; 1822 Glyph *gp; 1823 1824 if (x1 > x2) 1825 temp = x1, x1 = x2, x2 = temp; 1826 if (y1 > y2) 1827 temp = y1, y1 = y2, y2 = temp; 1828 1829 LIMIT(x1, 0, term.col-1); 1830 LIMIT(x2, 0, term.col-1); 1831 LIMIT(y1, 0, term.row-1); 1832 LIMIT(y2, 0, term.row-1); 1833 1834 for (y = y1; y <= y2; y++) { 1835 term.dirty[y] = 1; 1836 for (x = x1; x <= x2; x++) { 1837 gp = &term.line[y][x]; 1838 if (selected(x, y)) 1839 selclear(); 1840 gp->fg = term.c.attr.fg; 1841 gp->bg = term.c.attr.bg; 1842 gp->mode = 0; 1843 gp->u = ' '; 1844 } 1845 } 1846 } 1847 1848 void 1849 tdeletechar(int n) 1850 { 1851 int dst, src, size; 1852 Glyph *line; 1853 1854 LIMIT(n, 0, term.col - term.c.x); 1855 1856 dst = term.c.x; 1857 src = term.c.x + n; 1858 size = term.col - src; 1859 line = term.line[term.c.y]; 1860 1861 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1862 tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); 1863 } 1864 1865 void 1866 tinsertblank(int n) 1867 { 1868 int dst, src, size; 1869 Glyph *line; 1870 1871 LIMIT(n, 0, term.col - term.c.x); 1872 1873 dst = term.c.x + n; 1874 src = term.c.x; 1875 size = term.col - dst; 1876 line = term.line[term.c.y]; 1877 1878 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1879 tclearregion(src, term.c.y, dst - 1, term.c.y); 1880 } 1881 1882 void 1883 tinsertblankline(int n) 1884 { 1885 if (BETWEEN(term.c.y, term.top, term.bot)) 1886 tscrolldown(term.c.y, n); 1887 } 1888 1889 void 1890 tdeleteline(int n) 1891 { 1892 if (BETWEEN(term.c.y, term.top, term.bot)) 1893 tscrollup(term.c.y, n); 1894 } 1895 1896 int32_t 1897 tdefcolor(int *attr, int *npar, int l) 1898 { 1899 int32_t idx = -1; 1900 uint r, g, b; 1901 1902 switch (attr[*npar + 1]) { 1903 case 2: /* direct color in RGB space */ 1904 if (*npar + 4 >= l) { 1905 fprintf(stderr, 1906 "erresc(38): Incorrect number of parameters (%d)\n", 1907 *npar); 1908 break; 1909 } 1910 r = attr[*npar + 2]; 1911 g = attr[*npar + 3]; 1912 b = attr[*npar + 4]; 1913 *npar += 4; 1914 if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) 1915 fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", 1916 r, g, b); 1917 else 1918 idx = TRUECOLOR(r, g, b); 1919 break; 1920 case 5: /* indexed color */ 1921 if (*npar + 2 >= l) { 1922 fprintf(stderr, 1923 "erresc(38): Incorrect number of parameters (%d)\n", 1924 *npar); 1925 break; 1926 } 1927 *npar += 2; 1928 if (!BETWEEN(attr[*npar], 0, 255)) 1929 fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); 1930 else 1931 idx = attr[*npar]; 1932 break; 1933 case 0: /* implemented defined (only foreground) */ 1934 case 1: /* transparent */ 1935 case 3: /* direct color in CMY space */ 1936 case 4: /* direct color in CMYK space */ 1937 default: 1938 fprintf(stderr, 1939 "erresc(38): gfx attr %d unknown\n", attr[*npar]); 1940 break; 1941 } 1942 1943 return idx; 1944 } 1945 1946 void 1947 tsetattr(int *attr, int l) 1948 { 1949 int i; 1950 int32_t idx; 1951 1952 for (i = 0; i < l; i++) { 1953 switch (attr[i]) { 1954 case 0: 1955 term.c.attr.mode &= ~( 1956 ATTR_BOLD | 1957 ATTR_FAINT | 1958 ATTR_ITALIC | 1959 ATTR_UNDERLINE | 1960 ATTR_BLINK | 1961 ATTR_REVERSE | 1962 ATTR_INVISIBLE | 1963 ATTR_STRUCK ); 1964 term.c.attr.fg = defaultfg; 1965 term.c.attr.bg = defaultbg; 1966 break; 1967 case 1: 1968 term.c.attr.mode |= ATTR_BOLD; 1969 break; 1970 case 2: 1971 term.c.attr.mode |= ATTR_FAINT; 1972 break; 1973 case 3: 1974 term.c.attr.mode |= ATTR_ITALIC; 1975 break; 1976 case 4: 1977 term.c.attr.mode |= ATTR_UNDERLINE; 1978 break; 1979 case 5: /* slow blink */ 1980 /* FALLTHROUGH */ 1981 case 6: /* rapid blink */ 1982 term.c.attr.mode |= ATTR_BLINK; 1983 break; 1984 case 7: 1985 term.c.attr.mode |= ATTR_REVERSE; 1986 break; 1987 case 8: 1988 term.c.attr.mode |= ATTR_INVISIBLE; 1989 break; 1990 case 9: 1991 term.c.attr.mode |= ATTR_STRUCK; 1992 break; 1993 case 22: 1994 term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); 1995 break; 1996 case 23: 1997 term.c.attr.mode &= ~ATTR_ITALIC; 1998 break; 1999 case 24: 2000 term.c.attr.mode &= ~ATTR_UNDERLINE; 2001 break; 2002 case 25: 2003 term.c.attr.mode &= ~ATTR_BLINK; 2004 break; 2005 case 27: 2006 term.c.attr.mode &= ~ATTR_REVERSE; 2007 break; 2008 case 28: 2009 term.c.attr.mode &= ~ATTR_INVISIBLE; 2010 break; 2011 case 29: 2012 term.c.attr.mode &= ~ATTR_STRUCK; 2013 break; 2014 case 38: 2015 if ((idx = tdefcolor(attr, &i, l)) >= 0) 2016 term.c.attr.fg = idx; 2017 break; 2018 case 39: 2019 term.c.attr.fg = defaultfg; 2020 break; 2021 case 48: 2022 if ((idx = tdefcolor(attr, &i, l)) >= 0) 2023 term.c.attr.bg = idx; 2024 break; 2025 case 49: 2026 term.c.attr.bg = defaultbg; 2027 break; 2028 default: 2029 if (BETWEEN(attr[i], 30, 37)) { 2030 term.c.attr.fg = attr[i] - 30; 2031 } else if (BETWEEN(attr[i], 40, 47)) { 2032 term.c.attr.bg = attr[i] - 40; 2033 } else if (BETWEEN(attr[i], 90, 97)) { 2034 term.c.attr.fg = attr[i] - 90 + 8; 2035 } else if (BETWEEN(attr[i], 100, 107)) { 2036 term.c.attr.bg = attr[i] - 100 + 8; 2037 } else { 2038 fprintf(stderr, 2039 "erresc(default): gfx attr %d unknown\n", 2040 attr[i]), csidump(); 2041 } 2042 break; 2043 } 2044 } 2045 } 2046 2047 void 2048 tsetscroll(int t, int b) 2049 { 2050 int temp; 2051 2052 LIMIT(t, 0, term.row-1); 2053 LIMIT(b, 0, term.row-1); 2054 if (t > b) { 2055 temp = t; 2056 t = b; 2057 b = temp; 2058 } 2059 term.top = t; 2060 term.bot = b; 2061 } 2062 2063 void 2064 tsetmode(int priv, int set, int *args, int narg) 2065 { 2066 int *lim, mode; 2067 int alt; 2068 2069 for (lim = args + narg; args < lim; ++args) { 2070 if (priv) { 2071 switch (*args) { 2072 case 1: /* DECCKM -- Cursor key */ 2073 MODBIT(term.mode, set, MODE_APPCURSOR); 2074 break; 2075 case 5: /* DECSCNM -- Reverse video */ 2076 mode = term.mode; 2077 MODBIT(term.mode, set, MODE_REVERSE); 2078 if (mode != term.mode) 2079 redraw(); 2080 break; 2081 case 6: /* DECOM -- Origin */ 2082 MODBIT(term.c.state, set, CURSOR_ORIGIN); 2083 tmoveato(0, 0); 2084 break; 2085 case 7: /* DECAWM -- Auto wrap */ 2086 MODBIT(term.mode, set, MODE_WRAP); 2087 break; 2088 case 0: /* Error (IGNORED) */ 2089 case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ 2090 case 3: /* DECCOLM -- Column (IGNORED) */ 2091 case 4: /* DECSCLM -- Scroll (IGNORED) */ 2092 case 8: /* DECARM -- Auto repeat (IGNORED) */ 2093 case 18: /* DECPFF -- Printer feed (IGNORED) */ 2094 case 19: /* DECPEX -- Printer extent (IGNORED) */ 2095 case 42: /* DECNRCM -- National characters (IGNORED) */ 2096 case 12: /* att610 -- Start blinking cursor (IGNORED) */ 2097 break; 2098 case 25: /* DECTCEM -- Text Cursor Enable Mode */ 2099 MODBIT(term.mode, !set, MODE_HIDE); 2100 break; 2101 case 9: /* X10 mouse compatibility mode */ 2102 MODBIT(term.mode, 0, MODE_MOUSE); 2103 MODBIT(term.mode, set, MODE_MOUSEX10); 2104 break; 2105 case 1000: /* 1000: report button press */ 2106 MODBIT(term.mode, 0, MODE_MOUSE); 2107 MODBIT(term.mode, set, MODE_MOUSEBTN); 2108 break; 2109 case 1002: /* 1002: report motion on button press */ 2110 MODBIT(term.mode, 0, MODE_MOUSE); 2111 MODBIT(term.mode, set, MODE_MOUSEMOTION); 2112 break; 2113 case 1003: /* 1003: enable all mouse motions */ 2114 MODBIT(term.mode, 0, MODE_MOUSE); 2115 MODBIT(term.mode, set, MODE_MOUSEMANY); 2116 break; 2117 case 1004: /* 1004: send focus events to tty */ 2118 MODBIT(term.mode, set, MODE_FOCUS); 2119 break; 2120 case 1006: /* 1006: extended reporting mode */ 2121 MODBIT(term.mode, set, MODE_MOUSESGR); 2122 break; 2123 case 1034: 2124 MODBIT(term.mode, set, MODE_8BIT); 2125 break; 2126 case 1049: /* swap screen & set/restore cursor as xterm */ 2127 if (!allowaltscreen) 2128 break; 2129 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); 2130 /* FALLTHROUGH */ 2131 case 47: /* swap screen */ 2132 case 1047: 2133 if (!allowaltscreen) 2134 break; 2135 alt = IS_SET(MODE_ALTSCREEN); 2136 if (alt) { 2137 tclearregion(0, 0, term.col-1, 2138 term.row-1); 2139 } 2140 if (set ^ alt) /* set is always 1 or 0 */ 2141 tswapscreen(); 2142 if (*args != 1049) 2143 break; 2144 /* FALLTHROUGH */ 2145 case 1048: 2146 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); 2147 break; 2148 case 2004: /* 2004: bracketed paste mode */ 2149 MODBIT(term.mode, set, MODE_BRCKTPASTE); 2150 break; 2151 /* Not implemented mouse modes. See comments there. */ 2152 case 1001: /* mouse highlight mode; can hang the 2153 terminal by design when implemented. */ 2154 case 1005: /* UTF-8 mouse mode; will confuse 2155 applications not supporting UTF-8 2156 and luit. */ 2157 case 1015: /* urxvt mangled mouse mode; incompatible 2158 and can be mistaken for other control 2159 codes. */ 2160 default: 2161 fprintf(stderr, 2162 "erresc: unknown private set/reset mode %d\n", 2163 *args); 2164 break; 2165 } 2166 } else { 2167 switch (*args) { 2168 case 0: /* Error (IGNORED) */ 2169 break; 2170 case 2: /* KAM -- keyboard action */ 2171 MODBIT(term.mode, set, MODE_KBDLOCK); 2172 break; 2173 case 4: /* IRM -- Insertion-replacement */ 2174 MODBIT(term.mode, set, MODE_INSERT); 2175 break; 2176 case 12: /* SRM -- Send/Receive */ 2177 MODBIT(term.mode, !set, MODE_ECHO); 2178 break; 2179 case 20: /* LNM -- Linefeed/new line */ 2180 MODBIT(term.mode, set, MODE_CRLF); 2181 break; 2182 default: 2183 fprintf(stderr, 2184 "erresc: unknown set/reset mode %d\n", 2185 *args); 2186 break; 2187 } 2188 } 2189 } 2190 } 2191 2192 void 2193 csihandle(void) 2194 { 2195 char buf[40]; 2196 int len; 2197 2198 switch (csiescseq.mode[0]) { 2199 default: 2200 unknown: 2201 fprintf(stderr, "erresc: unknown csi "); 2202 csidump(); 2203 /* die(""); */ 2204 break; 2205 case '@': /* ICH -- Insert <n> blank char */ 2206 DEFAULT(csiescseq.arg[0], 1); 2207 tinsertblank(csiescseq.arg[0]); 2208 break; 2209 case 'A': /* CUU -- Cursor <n> Up */ 2210 DEFAULT(csiescseq.arg[0], 1); 2211 tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); 2212 break; 2213 case 'B': /* CUD -- Cursor <n> Down */ 2214 case 'e': /* VPR --Cursor <n> Down */ 2215 DEFAULT(csiescseq.arg[0], 1); 2216 tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); 2217 break; 2218 case 'i': /* MC -- Media Copy */ 2219 switch (csiescseq.arg[0]) { 2220 case 0: 2221 tdump(); 2222 break; 2223 case 1: 2224 tdumpline(term.c.y); 2225 break; 2226 case 2: 2227 tdumpsel(); 2228 break; 2229 case 4: 2230 term.mode &= ~MODE_PRINT; 2231 break; 2232 case 5: 2233 term.mode |= MODE_PRINT; 2234 break; 2235 } 2236 break; 2237 case 'c': /* DA -- Device Attributes */ 2238 if (csiescseq.arg[0] == 0) 2239 ttywrite(vtiden, sizeof(vtiden) - 1); 2240 break; 2241 case 'C': /* CUF -- Cursor <n> Forward */ 2242 case 'a': /* HPR -- Cursor <n> Forward */ 2243 DEFAULT(csiescseq.arg[0], 1); 2244 tmoveto(term.c.x+csiescseq.arg[0], term.c.y); 2245 break; 2246 case 'D': /* CUB -- Cursor <n> Backward */ 2247 DEFAULT(csiescseq.arg[0], 1); 2248 tmoveto(term.c.x-csiescseq.arg[0], term.c.y); 2249 break; 2250 case 'E': /* CNL -- Cursor <n> Down and first col */ 2251 DEFAULT(csiescseq.arg[0], 1); 2252 tmoveto(0, term.c.y+csiescseq.arg[0]); 2253 break; 2254 case 'F': /* CPL -- Cursor <n> Up and first col */ 2255 DEFAULT(csiescseq.arg[0], 1); 2256 tmoveto(0, term.c.y-csiescseq.arg[0]); 2257 break; 2258 case 'g': /* TBC -- Tabulation clear */ 2259 switch (csiescseq.arg[0]) { 2260 case 0: /* clear current tab stop */ 2261 term.tabs[term.c.x] = 0; 2262 break; 2263 case 3: /* clear all the tabs */ 2264 memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 2265 break; 2266 default: 2267 goto unknown; 2268 } 2269 break; 2270 case 'G': /* CHA -- Move to <col> */ 2271 case '`': /* HPA */ 2272 DEFAULT(csiescseq.arg[0], 1); 2273 tmoveto(csiescseq.arg[0]-1, term.c.y); 2274 break; 2275 case 'H': /* CUP -- Move to <row> <col> */ 2276 case 'f': /* HVP */ 2277 DEFAULT(csiescseq.arg[0], 1); 2278 DEFAULT(csiescseq.arg[1], 1); 2279 tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); 2280 break; 2281 case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */ 2282 DEFAULT(csiescseq.arg[0], 1); 2283 tputtab(csiescseq.arg[0]); 2284 break; 2285 case 'J': /* ED -- Clear screen */ 2286 selclear(); 2287 switch (csiescseq.arg[0]) { 2288 case 0: /* below */ 2289 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 2290 if (term.c.y < term.row-1) { 2291 tclearregion(0, term.c.y+1, term.col-1, 2292 term.row-1); 2293 } 2294 break; 2295 case 1: /* above */ 2296 if (term.c.y > 1) 2297 tclearregion(0, 0, term.col-1, term.c.y-1); 2298 tclearregion(0, term.c.y, term.c.x, term.c.y); 2299 break; 2300 case 2: /* all */ 2301 tclearregion(0, 0, term.col-1, term.row-1); 2302 break; 2303 default: 2304 goto unknown; 2305 } 2306 break; 2307 case 'K': /* EL -- Clear line */ 2308 switch (csiescseq.arg[0]) { 2309 case 0: /* right */ 2310 tclearregion(term.c.x, term.c.y, term.col-1, 2311 term.c.y); 2312 break; 2313 case 1: /* left */ 2314 tclearregion(0, term.c.y, term.c.x, term.c.y); 2315 break; 2316 case 2: /* all */ 2317 tclearregion(0, term.c.y, term.col-1, term.c.y); 2318 break; 2319 } 2320 break; 2321 case 'S': /* SU -- Scroll <n> line up */ 2322 DEFAULT(csiescseq.arg[0], 1); 2323 tscrollup(term.top, csiescseq.arg[0]); 2324 break; 2325 case 'T': /* SD -- Scroll <n> line down */ 2326 DEFAULT(csiescseq.arg[0], 1); 2327 tscrolldown(term.top, csiescseq.arg[0]); 2328 break; 2329 case 'L': /* IL -- Insert <n> blank lines */ 2330 DEFAULT(csiescseq.arg[0], 1); 2331 tinsertblankline(csiescseq.arg[0]); 2332 break; 2333 case 'l': /* RM -- Reset Mode */ 2334 tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); 2335 break; 2336 case 'M': /* DL -- Delete <n> lines */ 2337 DEFAULT(csiescseq.arg[0], 1); 2338 tdeleteline(csiescseq.arg[0]); 2339 break; 2340 case 'X': /* ECH -- Erase <n> char */ 2341 DEFAULT(csiescseq.arg[0], 1); 2342 tclearregion(term.c.x, term.c.y, 2343 term.c.x + csiescseq.arg[0] - 1, term.c.y); 2344 break; 2345 case 'P': /* DCH -- Delete <n> char */ 2346 DEFAULT(csiescseq.arg[0], 1); 2347 tdeletechar(csiescseq.arg[0]); 2348 break; 2349 case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */ 2350 DEFAULT(csiescseq.arg[0], 1); 2351 tputtab(-csiescseq.arg[0]); 2352 break; 2353 case 'd': /* VPA -- Move to <row> */ 2354 DEFAULT(csiescseq.arg[0], 1); 2355 tmoveato(term.c.x, csiescseq.arg[0]-1); 2356 break; 2357 case 'h': /* SM -- Set terminal mode */ 2358 tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); 2359 break; 2360 case 'm': /* SGR -- Terminal attribute (color) */ 2361 tsetattr(csiescseq.arg, csiescseq.narg); 2362 break; 2363 case 'n': /* DSR – Device Status Report (cursor position) */ 2364 if (csiescseq.arg[0] == 6) { 2365 len = snprintf(buf, sizeof(buf),"\033[%i;%iR", 2366 term.c.y+1, term.c.x+1); 2367 ttywrite(buf, len); 2368 } 2369 break; 2370 case 'r': /* DECSTBM -- Set Scrolling Region */ 2371 if (csiescseq.priv) { 2372 goto unknown; 2373 } else { 2374 DEFAULT(csiescseq.arg[0], 1); 2375 DEFAULT(csiescseq.arg[1], term.row); 2376 tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); 2377 tmoveato(0, 0); 2378 } 2379 break; 2380 case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 2381 tcursor(CURSOR_SAVE); 2382 break; 2383 case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 2384 tcursor(CURSOR_LOAD); 2385 break; 2386 case ' ': 2387 switch (csiescseq.mode[1]) { 2388 case 'q': /* DECSCUSR -- Set Cursor Style */ 2389 DEFAULT(csiescseq.arg[0], 1); 2390 if (!BETWEEN(csiescseq.arg[0], 0, 6)) { 2391 goto unknown; 2392 } 2393 wl.cursor = csiescseq.arg[0]; 2394 break; 2395 default: 2396 goto unknown; 2397 } 2398 break; 2399 } 2400 } 2401 2402 void 2403 csidump(void) 2404 { 2405 int i; 2406 uint c; 2407 2408 fprintf(stderr, "ESC["); 2409 for (i = 0; i < csiescseq.len; i++) { 2410 c = csiescseq.buf[i] & 0xff; 2411 if (isprint(c)) { 2412 putc(c, stderr); 2413 } else if (c == '\n') { 2414 fprintf(stderr, "(\\n)"); 2415 } else if (c == '\r') { 2416 fprintf(stderr, "(\\r)"); 2417 } else if (c == 0x1b) { 2418 fprintf(stderr, "(\\e)"); 2419 } else { 2420 fprintf(stderr, "(%02x)", c); 2421 } 2422 } 2423 putc('\n', stderr); 2424 } 2425 2426 void 2427 csireset(void) 2428 { 2429 memset(&csiescseq, 0, sizeof(csiescseq)); 2430 } 2431 2432 void 2433 strhandle(void) 2434 { 2435 char *p = NULL; 2436 int j, narg, par; 2437 2438 term.esc &= ~(ESC_STR_END|ESC_STR); 2439 strparse(); 2440 par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; 2441 2442 switch (strescseq.type) { 2443 case ']': /* OSC -- Operating System Command */ 2444 switch (par) { 2445 case 0: 2446 case 1: 2447 case 2: 2448 if (narg > 1) 2449 wlsettitle(strescseq.args[1]); 2450 return; 2451 case 4: /* color set */ 2452 if (narg < 3) 2453 break; 2454 p = strescseq.args[2]; 2455 /* FALLTHROUGH */ 2456 case 104: /* color reset, here p = NULL */ 2457 j = (narg > 1) ? atoi(strescseq.args[1]) : -1; 2458 if (wlsetcolorname(j, p)) { 2459 fprintf(stderr, "erresc: invalid color %s\n", p); 2460 } else { 2461 /* 2462 * TODO if defaultbg color is changed, borders 2463 * are dirty 2464 */ 2465 redraw(); 2466 } 2467 return; 2468 } 2469 break; 2470 case 'k': /* old title set compatibility */ 2471 wlsettitle(strescseq.args[0]); 2472 return; 2473 case 'P': /* DCS -- Device Control String */ 2474 term.mode |= ESC_DCS; 2475 case '_': /* APC -- Application Program Command */ 2476 case '^': /* PM -- Privacy Message */ 2477 return; 2478 } 2479 2480 fprintf(stderr, "erresc: unknown str "); 2481 strdump(); 2482 } 2483 2484 void 2485 strparse(void) 2486 { 2487 int c; 2488 char *p = strescseq.buf; 2489 2490 strescseq.narg = 0; 2491 strescseq.buf[strescseq.len] = '\0'; 2492 2493 if (*p == '\0') 2494 return; 2495 2496 while (strescseq.narg < STR_ARG_SIZ) { 2497 strescseq.args[strescseq.narg++] = p; 2498 while ((c = *p) != ';' && c != '\0') 2499 ++p; 2500 if (c == '\0') 2501 return; 2502 *p++ = '\0'; 2503 } 2504 } 2505 2506 void 2507 strdump(void) 2508 { 2509 int i; 2510 uint c; 2511 2512 fprintf(stderr, "ESC%c", strescseq.type); 2513 for (i = 0; i < strescseq.len; i++) { 2514 c = strescseq.buf[i] & 0xff; 2515 if (c == '\0') { 2516 putc('\n', stderr); 2517 return; 2518 } else if (isprint(c)) { 2519 putc(c, stderr); 2520 } else if (c == '\n') { 2521 fprintf(stderr, "(\\n)"); 2522 } else if (c == '\r') { 2523 fprintf(stderr, "(\\r)"); 2524 } else if (c == 0x1b) { 2525 fprintf(stderr, "(\\e)"); 2526 } else { 2527 fprintf(stderr, "(%02x)", c); 2528 } 2529 } 2530 fprintf(stderr, "ESC\\\n"); 2531 } 2532 2533 void 2534 strreset(void) 2535 { 2536 memset(&strescseq, 0, sizeof(strescseq)); 2537 } 2538 2539 void 2540 sendbreak(const Arg *arg) 2541 { 2542 if (tcsendbreak(cmdfd, 0)) 2543 perror("Error sending break"); 2544 } 2545 2546 void 2547 tprinter(char *s, size_t len) 2548 { 2549 if (iofd != -1 && xwrite(iofd, s, len) < 0) { 2550 fprintf(stderr, "Error writing in %s:%s\n", 2551 opt_io, strerror(errno)); 2552 close(iofd); 2553 iofd = -1; 2554 } 2555 } 2556 2557 void 2558 iso14755(const Arg *arg) 2559 { 2560 FILE *p; 2561 char *us, *e, codepoint[9], uc[UTF_SIZ]; 2562 unsigned long utf32; 2563 2564 if (!(p = popen(ISO14755CMD, "r"))) 2565 return; 2566 2567 us = fgets(codepoint, sizeof(codepoint), p); 2568 pclose(p); 2569 2570 if (!us || *us == '\0' || *us == '-' || strlen(us) > 7) 2571 return; 2572 if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || 2573 (*e != '\n' && *e != '\0')) 2574 return; 2575 2576 ttysend(uc, utf8encode(utf32, uc)); 2577 } 2578 2579 void 2580 toggleprinter(const Arg *arg) 2581 { 2582 term.mode ^= MODE_PRINT; 2583 } 2584 2585 void 2586 printscreen(const Arg *arg) 2587 { 2588 tdump(); 2589 } 2590 2591 void 2592 printsel(const Arg *arg) 2593 { 2594 tdumpsel(); 2595 } 2596 2597 void 2598 tdumpsel(void) 2599 { 2600 char *ptr; 2601 2602 if ((ptr = getsel())) { 2603 tprinter(ptr, strlen(ptr)); 2604 free(ptr); 2605 } 2606 } 2607 2608 void 2609 tdumpline(int n) 2610 { 2611 char buf[UTF_SIZ]; 2612 Glyph *bp, *end; 2613 2614 bp = &term.line[n][0]; 2615 end = &bp[MIN(tlinelen(n), term.col) - 1]; 2616 if (bp != end || bp->u != ' ') { 2617 for ( ;bp <= end; ++bp) 2618 tprinter(buf, utf8encode(bp->u, buf)); 2619 } 2620 tprinter("\n", 1); 2621 } 2622 2623 void 2624 tdump(void) 2625 { 2626 int i; 2627 2628 for (i = 0; i < term.row; ++i) 2629 tdumpline(i); 2630 } 2631 2632 void 2633 tputtab(int n) 2634 { 2635 uint x = term.c.x; 2636 2637 if (n > 0) { 2638 while (x < term.col && n--) 2639 for (++x; x < term.col && !term.tabs[x]; ++x) 2640 /* nothing */ ; 2641 } else if (n < 0) { 2642 while (x > 0 && n++) 2643 for (--x; x > 0 && !term.tabs[x]; --x) 2644 /* nothing */ ; 2645 } 2646 term.c.x = LIMIT(x, 0, term.col-1); 2647 } 2648 2649 void 2650 techo(Rune u) 2651 { 2652 if (ISCONTROL(u)) { /* control code */ 2653 if (u & 0x80) { 2654 u &= 0x7f; 2655 tputc('^'); 2656 tputc('['); 2657 } else if (u != '\n' && u != '\r' && u != '\t') { 2658 u ^= 0x40; 2659 tputc('^'); 2660 } 2661 } 2662 tputc(u); 2663 needdraw = true; 2664 } 2665 2666 void 2667 tdefutf8(char ascii) 2668 { 2669 if (ascii == 'G') 2670 term.mode |= MODE_UTF8; 2671 else if (ascii == '@') 2672 term.mode &= ~MODE_UTF8; 2673 } 2674 2675 void 2676 tdeftran(char ascii) 2677 { 2678 static char cs[] = "0B"; 2679 static int vcs[] = {CS_GRAPHIC0, CS_USA}; 2680 char *p; 2681 2682 if ((p = strchr(cs, ascii)) == NULL) { 2683 fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); 2684 } else { 2685 term.trantbl[term.icharset] = vcs[p - cs]; 2686 } 2687 } 2688 2689 void 2690 tdectest(char c) 2691 { 2692 int x, y; 2693 2694 if (c == '8') { /* DEC screen alignment test. */ 2695 for (x = 0; x < term.col; ++x) { 2696 for (y = 0; y < term.row; ++y) 2697 tsetchar('E', &term.c.attr, x, y); 2698 } 2699 } 2700 } 2701 2702 void 2703 tstrsequence(uchar c) 2704 { 2705 strreset(); 2706 2707 switch (c) { 2708 case 0x90: /* DCS -- Device Control String */ 2709 c = 'P'; 2710 term.esc |= ESC_DCS; 2711 break; 2712 case 0x9f: /* APC -- Application Program Command */ 2713 c = '_'; 2714 break; 2715 case 0x9e: /* PM -- Privacy Message */ 2716 c = '^'; 2717 break; 2718 case 0x9d: /* OSC -- Operating System Command */ 2719 c = ']'; 2720 break; 2721 } 2722 strescseq.type = c; 2723 term.esc |= ESC_STR; 2724 } 2725 2726 void 2727 tcontrolcode(uchar ascii) 2728 { 2729 switch (ascii) { 2730 case '\t': /* HT */ 2731 tputtab(1); 2732 return; 2733 case '\b': /* BS */ 2734 tmoveto(term.c.x-1, term.c.y); 2735 return; 2736 case '\r': /* CR */ 2737 tmoveto(0, term.c.y); 2738 return; 2739 case '\f': /* LF */ 2740 case '\v': /* VT */ 2741 case '\n': /* LF */ 2742 /* go to first col if the mode is set */ 2743 tnewline(IS_SET(MODE_CRLF)); 2744 return; 2745 case '\a': /* BEL */ 2746 if (term.esc & ESC_STR_END) { 2747 /* backwards compatibility to xterm */ 2748 strhandle(); 2749 } else { 2750 if (!(wl.state & WIN_FOCUSED)) 2751 wlseturgency(1); 2752 /* XXX: No bell on wayland 2753 * if (bellvolume) 2754 * XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); 2755 */ 2756 } 2757 break; 2758 case '\033': /* ESC */ 2759 csireset(); 2760 term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); 2761 term.esc |= ESC_START; 2762 return; 2763 case '\016': /* SO (LS1 -- Locking shift 1) */ 2764 case '\017': /* SI (LS0 -- Locking shift 0) */ 2765 term.charset = 1 - (ascii - '\016'); 2766 return; 2767 case '\032': /* SUB */ 2768 tsetchar('?', &term.c.attr, term.c.x, term.c.y); 2769 case '\030': /* CAN */ 2770 csireset(); 2771 break; 2772 case '\005': /* ENQ (IGNORED) */ 2773 case '\000': /* NUL (IGNORED) */ 2774 case '\021': /* XON (IGNORED) */ 2775 case '\023': /* XOFF (IGNORED) */ 2776 case 0177: /* DEL (IGNORED) */ 2777 return; 2778 case 0x80: /* TODO: PAD */ 2779 case 0x81: /* TODO: HOP */ 2780 case 0x82: /* TODO: BPH */ 2781 case 0x83: /* TODO: NBH */ 2782 case 0x84: /* TODO: IND */ 2783 break; 2784 case 0x85: /* NEL -- Next line */ 2785 tnewline(1); /* always go to first col */ 2786 break; 2787 case 0x86: /* TODO: SSA */ 2788 case 0x87: /* TODO: ESA */ 2789 break; 2790 case 0x88: /* HTS -- Horizontal tab stop */ 2791 term.tabs[term.c.x] = 1; 2792 break; 2793 case 0x89: /* TODO: HTJ */ 2794 case 0x8a: /* TODO: VTS */ 2795 case 0x8b: /* TODO: PLD */ 2796 case 0x8c: /* TODO: PLU */ 2797 case 0x8d: /* TODO: RI */ 2798 case 0x8e: /* TODO: SS2 */ 2799 case 0x8f: /* TODO: SS3 */ 2800 case 0x91: /* TODO: PU1 */ 2801 case 0x92: /* TODO: PU2 */ 2802 case 0x93: /* TODO: STS */ 2803 case 0x94: /* TODO: CCH */ 2804 case 0x95: /* TODO: MW */ 2805 case 0x96: /* TODO: SPA */ 2806 case 0x97: /* TODO: EPA */ 2807 case 0x98: /* TODO: SOS */ 2808 case 0x99: /* TODO: SGCI */ 2809 break; 2810 case 0x9a: /* DECID -- Identify Terminal */ 2811 ttywrite(vtiden, sizeof(vtiden) - 1); 2812 break; 2813 case 0x9b: /* TODO: CSI */ 2814 case 0x9c: /* TODO: ST */ 2815 break; 2816 case 0x90: /* DCS -- Device Control String */ 2817 case 0x9d: /* OSC -- Operating System Command */ 2818 case 0x9e: /* PM -- Privacy Message */ 2819 case 0x9f: /* APC -- Application Program Command */ 2820 tstrsequence(ascii); 2821 return; 2822 } 2823 /* only CAN, SUB, \a and C1 chars interrupt a sequence */ 2824 term.esc &= ~(ESC_STR_END|ESC_STR); 2825 } 2826 2827 /* 2828 * returns 1 when the sequence is finished and it hasn't to read 2829 * more characters for this sequence, otherwise 0 2830 */ 2831 int 2832 eschandle(uchar ascii) 2833 { 2834 switch (ascii) { 2835 case '[': 2836 term.esc |= ESC_CSI; 2837 return 0; 2838 case '#': 2839 term.esc |= ESC_TEST; 2840 return 0; 2841 case '%': 2842 term.esc |= ESC_UTF8; 2843 return 0; 2844 case 'P': /* DCS -- Device Control String */ 2845 case '_': /* APC -- Application Program Command */ 2846 case '^': /* PM -- Privacy Message */ 2847 case ']': /* OSC -- Operating System Command */ 2848 case 'k': /* old title set compatibility */ 2849 tstrsequence(ascii); 2850 return 0; 2851 case 'n': /* LS2 -- Locking shift 2 */ 2852 case 'o': /* LS3 -- Locking shift 3 */ 2853 term.charset = 2 + (ascii - 'n'); 2854 break; 2855 case '(': /* GZD4 -- set primary charset G0 */ 2856 case ')': /* G1D4 -- set secondary charset G1 */ 2857 case '*': /* G2D4 -- set tertiary charset G2 */ 2858 case '+': /* G3D4 -- set quaternary charset G3 */ 2859 term.icharset = ascii - '('; 2860 term.esc |= ESC_ALTCHARSET; 2861 return 0; 2862 case 'D': /* IND -- Linefeed */ 2863 if (term.c.y == term.bot) { 2864 tscrollup(term.top, 1); 2865 } else { 2866 tmoveto(term.c.x, term.c.y+1); 2867 } 2868 break; 2869 case 'E': /* NEL -- Next line */ 2870 tnewline(1); /* always go to first col */ 2871 break; 2872 case 'H': /* HTS -- Horizontal tab stop */ 2873 term.tabs[term.c.x] = 1; 2874 break; 2875 case 'M': /* RI -- Reverse index */ 2876 if (term.c.y == term.top) { 2877 tscrolldown(term.top, 1); 2878 } else { 2879 tmoveto(term.c.x, term.c.y-1); 2880 } 2881 break; 2882 case 'Z': /* DECID -- Identify Terminal */ 2883 ttywrite(vtiden, sizeof(vtiden) - 1); 2884 break; 2885 case 'c': /* RIS -- Reset to inital state */ 2886 treset(); 2887 wlresettitle(); 2888 wlloadcols(); 2889 break; 2890 case '=': /* DECPAM -- Application keypad */ 2891 term.mode |= MODE_APPKEYPAD; 2892 break; 2893 case '>': /* DECPNM -- Normal keypad */ 2894 term.mode &= ~MODE_APPKEYPAD; 2895 break; 2896 case '7': /* DECSC -- Save Cursor */ 2897 tcursor(CURSOR_SAVE); 2898 break; 2899 case '8': /* DECRC -- Restore Cursor */ 2900 tcursor(CURSOR_LOAD); 2901 break; 2902 case '\\': /* ST -- String Terminator */ 2903 if (term.esc & ESC_STR_END) 2904 strhandle(); 2905 break; 2906 default: 2907 fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", 2908 (uchar) ascii, isprint(ascii)? ascii:'.'); 2909 break; 2910 } 2911 return 1; 2912 } 2913 2914 void 2915 tputc(Rune u) 2916 { 2917 char c[UTF_SIZ]; 2918 int control; 2919 int width, len; 2920 Glyph *gp; 2921 2922 control = ISCONTROL(u); 2923 if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 2924 c[0] = u; 2925 width = len = 1; 2926 } else { 2927 len = utf8encode(u, c); 2928 if (!control && (width = wcwidth(u)) == -1) { 2929 memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ 2930 width = 1; 2931 } 2932 } 2933 2934 if (IS_SET(MODE_PRINT)) 2935 tprinter(c, len); 2936 2937 /* 2938 * STR sequence must be checked before anything else 2939 * because it uses all following characters until it 2940 * receives a ESC, a SUB, a ST or any other C1 control 2941 * character. 2942 */ 2943 if (term.esc & ESC_STR) { 2944 if (u == '\a' || u == 030 || u == 032 || u == 033 || 2945 ISCONTROLC1(u)) { 2946 term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); 2947 if (IS_SET(MODE_SIXEL)) { 2948 /* TODO: render sixel */; 2949 term.mode &= ~MODE_SIXEL; 2950 return; 2951 } 2952 term.esc |= ESC_STR_END; 2953 goto check_control_code; 2954 } 2955 2956 2957 if (IS_SET(MODE_SIXEL)) { 2958 /* TODO: implement sixel mode */ 2959 return; 2960 } 2961 if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') 2962 term.mode |= MODE_SIXEL; 2963 2964 if (strescseq.len+len >= sizeof(strescseq.buf)-1) { 2965 /* 2966 * Here is a bug in terminals. If the user never sends 2967 * some code to stop the str or esc command, then st 2968 * will stop responding. But this is better than 2969 * silently failing with unknown characters. At least 2970 * then users will report back. 2971 * 2972 * In the case users ever get fixed, here is the code: 2973 */ 2974 /* 2975 * term.esc = 0; 2976 * strhandle(); 2977 */ 2978 return; 2979 } 2980 2981 memmove(&strescseq.buf[strescseq.len], c, len); 2982 strescseq.len += len; 2983 return; 2984 } 2985 2986 check_control_code: 2987 /* 2988 * Actions of control codes must be performed as soon they arrive 2989 * because they can be embedded inside a control sequence, and 2990 * they must not cause conflicts with sequences. 2991 */ 2992 if (control) { 2993 tcontrolcode(u); 2994 /* 2995 * control codes are not shown ever 2996 */ 2997 return; 2998 } else if (term.esc & ESC_START) { 2999 if (term.esc & ESC_CSI) { 3000 csiescseq.buf[csiescseq.len++] = u; 3001 if (BETWEEN(u, 0x40, 0x7E) 3002 || csiescseq.len >= \ 3003 sizeof(csiescseq.buf)-1) { 3004 term.esc = 0; 3005 csiparse(); 3006 csihandle(); 3007 } 3008 return; 3009 } else if (term.esc & ESC_UTF8) { 3010 tdefutf8(u); 3011 } else if (term.esc & ESC_ALTCHARSET) { 3012 tdeftran(u); 3013 } else if (term.esc & ESC_TEST) { 3014 tdectest(u); 3015 } else { 3016 if (!eschandle(u)) 3017 return; 3018 /* sequence already finished */ 3019 } 3020 term.esc = 0; 3021 /* 3022 * All characters which form part of a sequence are not 3023 * printed 3024 */ 3025 return; 3026 } 3027 if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) 3028 selclear(); 3029 3030 gp = &term.line[term.c.y][term.c.x]; 3031 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { 3032 gp->mode |= ATTR_WRAP; 3033 tnewline(1); 3034 gp = &term.line[term.c.y][term.c.x]; 3035 } 3036 3037 if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) 3038 memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); 3039 3040 if (term.c.x+width > term.col) { 3041 tnewline(1); 3042 gp = &term.line[term.c.y][term.c.x]; 3043 } 3044 3045 tsetchar(u, &term.c.attr, term.c.x, term.c.y); 3046 3047 if (width == 2) { 3048 gp->mode |= ATTR_WIDE; 3049 if (term.c.x+1 < term.col) { 3050 gp[1].u = '\0'; 3051 gp[1].mode = ATTR_WDUMMY; 3052 } 3053 } 3054 if (term.c.x+width < term.col) { 3055 tmoveto(term.c.x+width, term.c.y); 3056 } else { 3057 term.c.state |= CURSOR_WRAPNEXT; 3058 } 3059 } 3060 3061 void 3062 tresize(int col, int row) 3063 { 3064 int i; 3065 int minrow = MIN(row, term.row); 3066 int mincol = MIN(col, term.col); 3067 int *bp; 3068 TCursor c; 3069 3070 if (col < 1 || row < 1) { 3071 fprintf(stderr, 3072 "tresize: error resizing to %dx%d\n", col, row); 3073 return; 3074 } 3075 3076 /* 3077 * slide screen to keep cursor where we expect it - 3078 * tscrollup would work here, but we can optimize to 3079 * memmove because we're freeing the earlier lines 3080 */ 3081 for (i = 0; i <= term.c.y - row; i++) { 3082 free(term.line[i]); 3083 free(term.alt[i]); 3084 } 3085 /* ensure that both src and dst are not NULL */ 3086 if (i > 0) { 3087 memmove(term.line, term.line + i, row * sizeof(Line)); 3088 memmove(term.alt, term.alt + i, row * sizeof(Line)); 3089 } 3090 for (i += row; i < term.row; i++) { 3091 free(term.line[i]); 3092 free(term.alt[i]); 3093 } 3094 3095 /* resize to new height */ 3096 term.line = xrealloc(term.line, row * sizeof(Line)); 3097 term.alt = xrealloc(term.alt, row * sizeof(Line)); 3098 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); 3099 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 3100 3101 /* resize each row to new width, zero-pad if needed */ 3102 for (i = 0; i < minrow; i++) { 3103 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); 3104 term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); 3105 } 3106 3107 /* allocate any new rows */ 3108 for (/* i == minrow */; i < row; i++) { 3109 term.line[i] = xmalloc(col * sizeof(Glyph)); 3110 term.alt[i] = xmalloc(col * sizeof(Glyph)); 3111 } 3112 if (col > term.col) { 3113 bp = term.tabs + term.col; 3114 3115 memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); 3116 while (--bp > term.tabs && !*bp) 3117 /* nothing */ ; 3118 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) 3119 *bp = 1; 3120 } 3121 /* update terminal size */ 3122 term.col = col; 3123 term.row = row; 3124 /* reset scrolling region */ 3125 tsetscroll(0, row-1); 3126 /* make use of the LIMIT in tmoveto */ 3127 tmoveto(term.c.x, term.c.y); 3128 /* Clearing both screens (it makes dirty all lines) */ 3129 c = term.c; 3130 for (i = 0; i < 2; i++) { 3131 if (mincol < col && 0 < minrow) { 3132 tclearregion(mincol, 0, col - 1, minrow - 1); 3133 } 3134 if (0 < col && minrow < row) { 3135 tclearregion(0, minrow, col - 1, row - 1); 3136 } 3137 tswapscreen(); 3138 tcursor(CURSOR_LOAD); 3139 } 3140 term.c = c; 3141 } 3142 3143 void 3144 wlresize(int col, int row) 3145 { 3146 union wld_object object; 3147 3148 wl.tw = MAX(1, col * wl.cw); 3149 wl.th = MAX(1, row * wl.ch); 3150 3151 wld.oldbuffer = wld.buffer; 3152 wld.buffer = wld_create_buffer(wld.ctx, wl.w, wl.h, 3153 WLD_FORMAT_XRGB8888, 0); 3154 wld_export(wld.buffer, WLD_WAYLAND_OBJECT_BUFFER, &object); 3155 wl.buffer = object.ptr; 3156 } 3157 3158 uchar 3159 sixd_to_8bit(int x) 3160 { 3161 return x == 0 ? 0 : 0x37 + 0x28 * x; 3162 } 3163 3164 int 3165 wlloadcolor(int i, const char *name, uint32_t *color) 3166 { 3167 if (!name) { 3168 if (BETWEEN(i, 16, 255)) { /* 256 color */ 3169 if (i < 6*6*6+16) { /* same colors as xterm */ 3170 *color = 0xff << 24 | sixd_to_8bit(((i-16)/36)%6) << 16 3171 | sixd_to_8bit(((i-16)/6)%6) << 8 3172 | sixd_to_8bit(((i-16)/1)%6); 3173 } else { /* greyscale */ 3174 *color = 0xff << 24 | (0x8 + 0xa * (i-(6*6*6+16))) * 0x10101; 3175 } 3176 return true; 3177 } else 3178 name = colorname[i]; 3179 } 3180 3181 return wld_lookup_named_color(name, color); 3182 } 3183 3184 void 3185 wlloadcols(void) 3186 { 3187 int i; 3188 3189 for (i = 0; i < LEN(dc.col); i++) 3190 if (!wlloadcolor(i, NULL, &dc.col[i])) { 3191 if (colorname[i]) 3192 die("Could not allocate color '%s'\n", colorname[i]); 3193 else 3194 die("Could not allocate color %d\n", i); 3195 } 3196 } 3197 3198 int 3199 wlsetcolorname(int x, const char *name) 3200 { 3201 uint32_t color; 3202 3203 if (!BETWEEN(x, 0, LEN(dc.col))) 3204 return 1; 3205 3206 if (!wlloadcolor(x, name, &color)) 3207 return 1; 3208 3209 dc.col[x] = color; 3210 3211 return 0; 3212 } 3213 3214 static void wlloadcursor(void) 3215 { 3216 char *names[] = { mouseshape, "xterm", "ibeam", "text" }; 3217 int i; 3218 3219 cursor.theme = wl_cursor_theme_load(NULL, 32, wl.shm); 3220 3221 for (i = 0; !cursor.cursor && i < LEN(names); i++) 3222 cursor.cursor = wl_cursor_theme_get_cursor(cursor.theme, names[i]); 3223 3224 cursor.surface = wl_compositor_create_surface(wl.cmp); 3225 } 3226 3227 /* 3228 * Absolute coordinates. 3229 */ 3230 void 3231 wlclear(int x1, int y1, int x2, int y2) 3232 { 3233 uint32_t color = dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg]; 3234 3235 wld_fill_rectangle(wld.renderer, color, x1, y1, x2 - x1, y2 - y1); 3236 } 3237 3238 int 3239 wlloadfont(Font *f, FcPattern *pattern) 3240 { 3241 FcPattern *configured; 3242 FcPattern *match; 3243 FcResult result; 3244 struct wld_extents extents; 3245 int wantattr, haveattr; 3246 3247 /* 3248 * Manually configure instead of calling XftMatchFont 3249 * so that we can use the configured pattern for 3250 * "missing glyph" lookups. 3251 */ 3252 configured = FcPatternDuplicate(pattern); 3253 if (!configured) 3254 return 1; 3255 3256 FcConfigSubstitute(NULL, configured, FcMatchPattern); 3257 FcDefaultSubstitute(configured); 3258 3259 match = FcFontMatch(NULL, configured, &result); 3260 if (!match) { 3261 FcPatternDestroy(configured); 3262 return 1; 3263 } 3264 3265 if (!(f->match = wld_font_open_pattern(wld.fontctx, match))) { 3266 FcPatternDestroy(configured); 3267 FcPatternDestroy(match); 3268 return 1; 3269 } 3270 3271 if ((FcPatternGetInteger(pattern, "slant", 0, &wantattr) == 3272 FcResultMatch)) { 3273 /* 3274 * Check if xft was unable to find a font with the appropriate 3275 * slant but gave us one anyway. Try to mitigate. 3276 */ 3277 if ((FcPatternGetInteger(match, "slant", 0, 3278 &haveattr) != FcResultMatch) || haveattr < wantattr) { 3279 f->badslant = 1; 3280 fputs("st: font slant does not match\n", stderr); 3281 } 3282 } 3283 3284 if ((FcPatternGetInteger(pattern, "weight", 0, &wantattr) == 3285 FcResultMatch)) { 3286 if ((FcPatternGetInteger(match, "weight", 0, 3287 &haveattr) != FcResultMatch) || haveattr != wantattr) { 3288 f->badweight = 1; 3289 fputs("st: font weight does not match\n", stderr); 3290 } 3291 } 3292 3293 3294 wld_font_text_extents(f->match, ascii_printable, &extents); 3295 3296 f->set = NULL; 3297 f->pattern = configured; 3298 3299 f->ascent = f->match->ascent; 3300 f->descent = f->match->descent; 3301 f->lbearing = 0; 3302 f->rbearing = f->match->max_advance; 3303 3304 f->height = f->ascent + f->descent; 3305 f->width = DIVCEIL(extents.advance, strlen(ascii_printable)); 3306 3307 return 0; 3308 } 3309 3310 void 3311 wlloadfonts(char *fontstr, double fontsize) 3312 { 3313 FcPattern *pattern; 3314 double fontval; 3315 float ceilf(float); 3316 3317 if (fontstr[0] == '-') { 3318 /* XXX: need XftXlfdParse equivalent */ 3319 pattern = NULL; 3320 } else { 3321 pattern = FcNameParse((FcChar8 *)fontstr); 3322 } 3323 3324 if (!pattern) 3325 die("st: can't open font %s\n", fontstr); 3326 3327 if (fontsize > 1) { 3328 FcPatternDel(pattern, FC_PIXEL_SIZE); 3329 FcPatternDel(pattern, FC_SIZE); 3330 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); 3331 usedfontsize = fontsize; 3332 } else { 3333 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == 3334 FcResultMatch) { 3335 usedfontsize = fontval; 3336 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == 3337 FcResultMatch) { 3338 usedfontsize = -1; 3339 } else { 3340 /* 3341 * Default font size is 12, if none given. This is to 3342 * have a known usedfontsize value. 3343 */ 3344 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); 3345 usedfontsize = 12; 3346 } 3347 defaultfontsize = usedfontsize; 3348 } 3349 3350 FcConfigSubstitute(0, pattern, FcMatchPattern); 3351 FcDefaultSubstitute(pattern); 3352 3353 if (wlloadfont(&dc.font, pattern)) 3354 die("st: can't open font %s\n", fontstr); 3355 3356 if (usedfontsize < 0) { 3357 FcPatternGetDouble(dc.font.pattern, 3358 FC_PIXEL_SIZE, 0, &fontval); 3359 usedfontsize = fontval; 3360 if (fontsize == 0) 3361 defaultfontsize = fontval; 3362 } 3363 3364 /* Setting character width and height. */ 3365 wl.cw = ceilf(dc.font.width * cwscale); 3366 wl.ch = ceilf(dc.font.height * chscale); 3367 3368 FcPatternDel(pattern, FC_SLANT); 3369 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); 3370 if (wlloadfont(&dc.ifont, pattern)) 3371 die("st: can't open font %s\n", fontstr); 3372 3373 FcPatternDel(pattern, FC_WEIGHT); 3374 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); 3375 if (wlloadfont(&dc.ibfont, pattern)) 3376 die("st: can't open font %s\n", fontstr); 3377 3378 FcPatternDel(pattern, FC_SLANT); 3379 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); 3380 if (wlloadfont(&dc.bfont, pattern)) 3381 die("st: can't open font %s\n", fontstr); 3382 3383 FcPatternDestroy(pattern); 3384 } 3385 3386 void 3387 wlunloadfont(Font *f) 3388 { 3389 wld_font_close(f->match); 3390 FcPatternDestroy(f->pattern); 3391 if (f->set) 3392 FcFontSetDestroy(f->set); 3393 } 3394 3395 void 3396 wlunloadfonts(void) 3397 { 3398 /* Free the loaded fonts in the font cache. */ 3399 while (frclen > 0) 3400 wld_font_close(frc[--frclen].font); 3401 3402 wlunloadfont(&dc.font); 3403 wlunloadfont(&dc.bfont); 3404 wlunloadfont(&dc.ifont); 3405 wlunloadfont(&dc.ibfont); 3406 } 3407 3408 void 3409 wlzoom(const Arg *arg) 3410 { 3411 Arg larg; 3412 3413 larg.f = usedfontsize + arg->f; 3414 wlzoomabs(&larg); 3415 } 3416 3417 void 3418 wlzoomabs(const Arg *arg) 3419 { 3420 wlunloadfonts(); 3421 wlloadfonts(usedfont, arg->f); 3422 cresize(0, 0); 3423 ttyresize(); 3424 redraw(); 3425 /* XXX: Should the window size be updated here because wayland doesn't 3426 * have a notion of hints? 3427 * xhints(); 3428 */ 3429 } 3430 3431 void 3432 wlzoomreset(const Arg *arg) 3433 { 3434 Arg larg; 3435 3436 if (defaultfontsize > 0) { 3437 larg.f = defaultfontsize; 3438 wlzoomabs(&larg); 3439 } 3440 } 3441 3442 void 3443 wlinit(void) 3444 { 3445 struct wl_registry *registry; 3446 3447 if (!(wl.dpy = wl_display_connect(NULL))) 3448 die("Can't open display\n"); 3449 3450 registry = wl_display_get_registry(wl.dpy); 3451 wl_registry_add_listener(registry, ®listener, NULL); 3452 wld.ctx = wld_wayland_create_context(wl.dpy, WLD_ANY); 3453 wld.renderer = wld_create_renderer(wld.ctx); 3454 3455 wl_display_roundtrip(wl.dpy); 3456 3457 if (!wl.shm) 3458 die("Display has no SHM\n"); 3459 if (!wl.seat) 3460 die("Display has no seat\n"); 3461 if (!wl.datadevmanager) 3462 die("Display has no data device manager\n"); 3463 if (!wl.wm) 3464 die("Display has no window manager\n"); 3465 if (!wl.timanager) 3466 die("Display has no text input manager\n"); 3467 3468 wl.keyboard = wl_seat_get_keyboard(wl.seat); 3469 wl_keyboard_add_listener(wl.keyboard, &kbdlistener, NULL); 3470 wl.pointer = wl_seat_get_pointer(wl.seat); 3471 wl_pointer_add_listener(wl.pointer, &ptrlistener, NULL); 3472 wl.datadev = wl_data_device_manager_get_data_device(wl.datadevmanager, 3473 wl.seat); 3474 wl_data_device_add_listener(wl.datadev, &datadevlistener, NULL); 3475 wl.textinput = zwp_text_input_manager_v3_get_text_input(wl.timanager, wl.seat); 3476 zwp_text_input_v3_add_listener(wl.textinput, &tilistener, NULL); 3477 3478 /* font */ 3479 if (!FcInit()) 3480 die("Could not init fontconfig.\n"); 3481 3482 usedfont = (opt_font == NULL)? font : opt_font; 3483 wld.fontctx = wld_font_create_context(); 3484 wlloadfonts(usedfont, 0); 3485 3486 wlloadcols(); 3487 wlloadcursor(); 3488 3489 wl.vis = 0; 3490 wl.h = 2 * borderpx + term.row * wl.ch; 3491 wl.w = 2 * borderpx + term.col * wl.cw; 3492 3493 wl.surface = wl_compositor_create_surface(wl.cmp); 3494 wl_surface_add_listener(wl.surface, &surflistener, NULL); 3495 3496 wl.xdgsurface = xdg_wm_base_get_xdg_surface(wl.wm, wl.surface); 3497 xdg_surface_add_listener(wl.xdgsurface, &xdgsurflistener, NULL); 3498 wl.toplevel = xdg_surface_get_toplevel(wl.xdgsurface); 3499 xdg_toplevel_add_listener(wl.toplevel, &toplevellistener, NULL); 3500 xdg_toplevel_set_app_id(wl.toplevel, opt_class ? opt_class : termname); 3501 3502 wl.xkb.ctx = xkb_context_new(0); 3503 wlresettitle(); 3504 } 3505 3506 /* 3507 * TODO: Implement something like XftDrawGlyphFontSpec in wld, and then apply a 3508 * similar patch to ae1923d27533ff46400d93765e971558201ca1ee 3509 */ 3510 3511 void 3512 wldraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) 3513 { 3514 int winx = borderpx + x * wl.cw, winy = borderpx + y * wl.ch, 3515 width = charlen * wl.cw, xp, i; 3516 int frcflags, charexists; 3517 int u8fl, u8fblen, u8cblen, doesexist; 3518 char *u8c, *u8fs; 3519 Rune unicodep; 3520 Font *font = &dc.font; 3521 FcResult fcres; 3522 FcPattern *fcpattern, *fontpattern; 3523 FcFontSet *fcsets[] = { NULL }; 3524 FcCharSet *fccharset; 3525 uint32_t fg, bg, temp; 3526 int oneatatime; 3527 3528 frcflags = FRC_NORMAL; 3529 3530 /* Fallback on color display for attributes not supported by the font */ 3531 if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { 3532 if (dc.ibfont.badslant || dc.ibfont.badweight) 3533 base.fg = defaultattr; 3534 font = &dc.ibfont; 3535 frcflags = FRC_ITALICBOLD; 3536 } else if (base.mode & ATTR_ITALIC) { 3537 if (dc.ifont.badslant) 3538 base.fg = defaultattr; 3539 font = &dc.ifont; 3540 frcflags = FRC_ITALIC; 3541 } else if (base.mode & ATTR_BOLD) { 3542 if (dc.bfont.badweight) 3543 base.fg = defaultattr; 3544 font = &dc.ifont; 3545 frcflags = FRC_BOLD; 3546 } 3547 3548 if (IS_TRUECOL(base.fg)) { 3549 fg = base.fg | 0xff000000; 3550 } else { 3551 fg = dc.col[base.fg]; 3552 } 3553 3554 if (IS_TRUECOL(base.bg)) { 3555 bg = base.bg | 0xff000000; 3556 } else { 3557 bg = dc.col[base.bg]; 3558 } 3559 3560 if (base.mode & ATTR_BOLD) { 3561 /* 3562 * change basic system colors [0-7] 3563 * to bright system colors [8-15] 3564 */ 3565 if (BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT)) 3566 fg = dc.col[base.fg + 8]; 3567 3568 if (base.mode & ATTR_ITALIC) { 3569 font = &dc.ibfont; 3570 frcflags = FRC_ITALICBOLD; 3571 } else { 3572 font = &dc.bfont; 3573 frcflags = FRC_BOLD; 3574 } 3575 } 3576 3577 if (IS_SET(MODE_REVERSE)) { 3578 if (fg == dc.col[defaultfg]) { 3579 fg = dc.col[defaultbg]; 3580 } else { 3581 fg = ~(fg & 0xffffff); 3582 } 3583 3584 if (bg == dc.col[defaultbg]) { 3585 bg = dc.col[defaultfg]; 3586 } else { 3587 bg = ~(bg & 0xffffff); 3588 } 3589 } 3590 3591 if (base.mode & ATTR_REVERSE) { 3592 temp = fg; 3593 fg = bg; 3594 bg = temp; 3595 } 3596 3597 if (base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) { 3598 fg = (fg & (0xff << 24)) 3599 | ((((fg >> 16) & 0xff) / 2) << 16) 3600 | ((((fg >> 8) & 0xff) / 2) << 8) 3601 | ((fg & 0xff) / 2); 3602 } 3603 3604 if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) 3605 fg = bg; 3606 3607 if (base.mode & ATTR_INVISIBLE) 3608 fg = bg; 3609 3610 /* Intelligent cleaning up of the borders. */ 3611 if (x == 0) { 3612 wlclear(0, (y == 0)? 0 : winy, borderpx, 3613 ((y >= term.row-1)? wl.h : (winy + wl.ch))); 3614 } 3615 if (x + charlen >= term.col) { 3616 wlclear(winx + width, (y == 0)? 0 : winy, wl.w, 3617 ((y >= term.row-1)? wl.h : (winy + wl.ch))); 3618 } 3619 if (y == 0) 3620 wlclear(winx, 0, winx + width, borderpx); 3621 if (y == term.row-1) 3622 wlclear(winx, winy + wl.ch, winx + width, wl.h); 3623 3624 /* Clean up the region we want to draw to. */ 3625 wld_fill_rectangle(wld.renderer, bg, winx, winy, width, wl.ch); 3626 3627 for (xp = winx; bytelen > 0;) { 3628 /* 3629 * Search for the range in the to be printed string of glyphs 3630 * that are in the main font. Then print that range. If 3631 * some glyph is found that is not in the font, do the 3632 * fallback dance. 3633 */ 3634 u8fs = s; 3635 u8fblen = 0; 3636 u8fl = 0; 3637 oneatatime = font->width != wl.cw; 3638 for (;;) { 3639 u8c = s; 3640 u8cblen = utf8decode(s, &unicodep, UTF_SIZ); 3641 s += u8cblen; 3642 bytelen -= u8cblen; 3643 3644 doesexist = wld_font_ensure_char(font->match, unicodep); 3645 if (doesexist) { 3646 u8fl++; 3647 u8fblen += u8cblen; 3648 if (!oneatatime && bytelen > 0) 3649 continue; 3650 } 3651 3652 if (u8fl > 0) { 3653 wld_draw_text(wld.renderer, 3654 font->match, fg, xp, 3655 winy + font->ascent, 3656 u8fs, u8fblen, NULL); 3657 xp += wl.cw * u8fl; 3658 } 3659 break; 3660 } 3661 if (doesexist) { 3662 if (oneatatime) 3663 continue; 3664 break; 3665 } 3666 3667 /* Search the font cache. */ 3668 for (i = 0; i < frclen; i++) { 3669 charexists = wld_font_ensure_char(frc[i].font, unicodep); 3670 /* Everything correct. */ 3671 if (charexists && frc[i].flags == frcflags) 3672 break; 3673 /* We got a default font for a not found glyph. */ 3674 if (!charexists && frc[i].flags == frcflags \ 3675 && frc[i].unicodep == unicodep) { 3676 break; 3677 } 3678 } 3679 3680 /* Nothing was found. */ 3681 if (i >= frclen) { 3682 if (!font->set) 3683 font->set = FcFontSort(0, font->pattern, 3684 1, 0, &fcres); 3685 fcsets[0] = font->set; 3686 3687 /* 3688 * Nothing was found in the cache. Now use 3689 * some dozen of Fontconfig calls to get the 3690 * font for one single character. 3691 * 3692 * Xft and fontconfig are design failures. 3693 */ 3694 fcpattern = FcPatternDuplicate(font->pattern); 3695 fccharset = FcCharSetCreate(); 3696 3697 FcCharSetAddChar(fccharset, unicodep); 3698 FcPatternAddCharSet(fcpattern, FC_CHARSET, 3699 fccharset); 3700 FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 3701 3702 FcConfigSubstitute(0, fcpattern, 3703 FcMatchPattern); 3704 FcDefaultSubstitute(fcpattern); 3705 3706 fontpattern = FcFontSetMatch(0, fcsets, 1, 3707 fcpattern, &fcres); 3708 3709 /* 3710 * Overwrite or create the new cache entry. 3711 */ 3712 if (frclen >= LEN(frc)) { 3713 frclen = LEN(frc) - 1; 3714 wld_font_close(frc[frclen].font); 3715 frc[frclen].unicodep = 0; 3716 } 3717 3718 frc[frclen].font = wld_font_open_pattern(wld.fontctx, 3719 fontpattern); 3720 frc[frclen].flags = frcflags; 3721 frc[frclen].unicodep = unicodep; 3722 3723 i = frclen; 3724 frclen++; 3725 3726 FcPatternDestroy(fcpattern); 3727 FcCharSetDestroy(fccharset); 3728 } 3729 3730 wld_draw_text(wld.renderer, frc[i].font, fg, 3731 xp, winy + frc[i].font->ascent, 3732 u8c, u8cblen, NULL); 3733 3734 xp += wl.cw * wcwidth(unicodep); 3735 } 3736 3737 if (base.mode & ATTR_UNDERLINE) { 3738 wld_fill_rectangle(wld.renderer, fg, winx, winy + font->ascent + 1, 3739 width, 1); 3740 } 3741 3742 if (base.mode & ATTR_STRUCK) { 3743 wld_fill_rectangle(wld.renderer, fg, winx, winy + 2 * font->ascent / 3, 3744 width, 1); 3745 } 3746 } 3747 3748 void 3749 wldrawglyph(Glyph g, int x, int y) 3750 { 3751 static char buf[UTF_SIZ]; 3752 size_t len = utf8encode(g.u, buf); 3753 int width = g.mode & ATTR_WIDE ? 2 : 1; 3754 3755 wldraws(buf, g, x, y, width, len); 3756 } 3757 3758 void 3759 wldrawcursor(void) 3760 { 3761 static int oldx = 0, oldy = 0; 3762 int curx; 3763 Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; 3764 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); 3765 uint32_t drawcol; 3766 3767 LIMIT(oldx, 0, term.col-1); 3768 LIMIT(oldy, 0, term.row-1); 3769 3770 curx = term.c.x; 3771 3772 /* adjust position if in dummy */ 3773 if (term.line[oldy][oldx].mode & ATTR_WDUMMY) 3774 oldx--; 3775 if (term.line[term.c.y][curx].mode & ATTR_WDUMMY) 3776 curx--; 3777 3778 /* remove the old cursor */ 3779 og = term.line[oldy][oldx]; 3780 if (ena_sel && selected(oldx, oldy)) 3781 og.mode ^= ATTR_REVERSE; 3782 wldrawglyph(og, oldx, oldy); 3783 if (oldx != curx || oldy != term.c.y) { 3784 wl_surface_damage(wl.surface, borderpx + oldx * wl.cw, 3785 borderpx + oldy * wl.ch, wl.cw, wl.ch); 3786 } 3787 3788 g.u = term.line[term.c.y][term.c.x].u; 3789 3790 /* 3791 * Select the right color for the right mode. 3792 */ 3793 if (IS_SET(MODE_REVERSE)) { 3794 g.mode |= ATTR_REVERSE; 3795 g.bg = defaultfg; 3796 if (ena_sel && selected(term.c.x, term.c.y)) { 3797 drawcol = dc.col[defaultcs]; 3798 g.fg = defaultrcs; 3799 } else { 3800 drawcol = dc.col[defaultrcs]; 3801 g.fg = defaultcs; 3802 } 3803 } else { 3804 if (ena_sel && selected(term.c.x, term.c.y)) { 3805 drawcol = dc.col[defaultrcs]; 3806 g.fg = defaultfg; 3807 g.bg = defaultrcs; 3808 } else { 3809 drawcol = dc.col[defaultcs]; 3810 } 3811 } 3812 3813 if (IS_SET(MODE_HIDE)) 3814 return; 3815 3816 /* draw the new one */ 3817 if (wl.state & WIN_FOCUSED) { 3818 switch (wl.cursor) { 3819 case 7: /* st extension: snowman */ 3820 utf8decode("☃", &g.u, UTF_SIZ); 3821 case 0: /* Blinking Block */ 3822 case 1: /* Blinking Block (Default) */ 3823 case 2: /* Steady Block */ 3824 g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; 3825 wldrawglyph(g, term.c.x, term.c.y); 3826 break; 3827 case 3: /* Blinking Underline */ 3828 case 4: /* Steady Underline */ 3829 wld_fill_rectangle(wld.renderer, drawcol, 3830 borderpx + curx * wl.cw, 3831 borderpx + (term.c.y + 1) * wl.ch - \ 3832 cursorthickness, 3833 wl.cw, cursorthickness); 3834 break; 3835 case 5: /* Blinking bar */ 3836 case 6: /* Steady bar */ 3837 wld_fill_rectangle(wld.renderer, drawcol, 3838 borderpx + curx * wl.cw, 3839 borderpx + term.c.y * wl.ch, 3840 cursorthickness, wl.ch); 3841 break; 3842 } 3843 } else { 3844 wld_fill_rectangle(wld.renderer, drawcol, 3845 borderpx + curx * wl.cw, 3846 borderpx + term.c.y * wl.ch, 3847 wl.cw - 1, 1); 3848 wld_fill_rectangle(wld.renderer, drawcol, 3849 borderpx + curx * wl.cw, 3850 borderpx + term.c.y * wl.ch, 3851 1, wl.ch - 1); 3852 wld_fill_rectangle(wld.renderer, drawcol, 3853 borderpx + (curx + 1) * wl.cw - 1, 3854 borderpx + term.c.y * wl.ch, 3855 1, wl.ch - 1); 3856 wld_fill_rectangle(wld.renderer, drawcol, 3857 borderpx + curx * wl.cw, 3858 borderpx + (term.c.y + 1) * wl.ch - 1, 3859 wl.cw, 1); 3860 } 3861 wl_surface_damage(wl.surface, borderpx + curx * wl.cw, 3862 borderpx + term.c.y * wl.ch, wl.cw, wl.ch); 3863 oldx = curx, oldy = term.c.y; 3864 } 3865 3866 void 3867 wlsettitle(char *title) 3868 { 3869 xdg_toplevel_set_title(wl.toplevel, title); 3870 } 3871 3872 void 3873 wlresettitle(void) 3874 { 3875 wlsettitle(opt_title ? opt_title : "st"); 3876 } 3877 3878 void 3879 redraw(void) 3880 { 3881 tfulldirt(); 3882 } 3883 3884 void 3885 draw(void) 3886 { 3887 int y, y0; 3888 3889 for (y = 0; y <= term.bot; ++y) { 3890 if (!term.dirty[y]) 3891 continue; 3892 for (y0 = y; y <= term.bot && term.dirty[y]; ++y); 3893 wl_surface_damage(wl.surface, 0, borderpx + y0 * wl.ch, 3894 wl.w, (y - y0) * wl.ch); 3895 } 3896 3897 wld_set_target_buffer(wld.renderer, wld.buffer); 3898 drawregion(0, 0, term.col, term.row); 3899 wl.framecb = wl_surface_frame(wl.surface); 3900 wl_callback_add_listener(wl.framecb, &framelistener, NULL); 3901 wld_flush(wld.renderer); 3902 wl_surface_attach(wl.surface, wl.buffer, 0, 0); 3903 wl_surface_commit(wl.surface); 3904 /* need to wait to destroy the old buffer until we commit the new 3905 * buffer */ 3906 if (wld.oldbuffer) { 3907 wld_buffer_unreference(wld.oldbuffer); 3908 wld.oldbuffer = 0; 3909 } 3910 needdraw = false; 3911 } 3912 3913 void 3914 drawregion(int x1, int y1, int x2, int y2) 3915 { 3916 int ic, ib, x, y, ox; 3917 Glyph base, new; 3918 char buf[DRAW_BUF_SIZ]; 3919 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); 3920 3921 for (y = y1; y < y2; y++) { 3922 if (!term.dirty[y]) 3923 continue; 3924 3925 term.dirty[y] = 0; 3926 base = term.line[y][0]; 3927 ic = ib = ox = 0; 3928 for (x = x1; x < x2; x++) { 3929 new = term.line[y][x]; 3930 if (new.mode == ATTR_WDUMMY) 3931 continue; 3932 if (ena_sel && selected(x, y)) 3933 new.mode ^= ATTR_REVERSE; 3934 if (ib > 0 && (ATTRCMP(base, new) 3935 || ib >= DRAW_BUF_SIZ-UTF_SIZ)) { 3936 wldraws(buf, base, ox, y, ic, ib); 3937 ic = ib = 0; 3938 } 3939 if (ib == 0) { 3940 ox = x; 3941 base = new; 3942 } 3943 3944 ib += utf8encode(new.u, buf+ib); 3945 ic += (new.mode & ATTR_WIDE)? 2 : 1; 3946 } 3947 if (ib > 0) 3948 wldraws(buf, base, ox, y, ic, ib); 3949 } 3950 wldrawcursor(); 3951 } 3952 3953 void 3954 wlseturgency(int add) 3955 { 3956 /* XXX: no urgency equivalent yet in wayland */ 3957 } 3958 3959 int 3960 match(uint mask, uint state) 3961 { 3962 return mask == MOD_MASK_ANY || mask == (state & ~(ignoremod)); 3963 } 3964 3965 void 3966 numlock(const Arg *dummy) 3967 { 3968 term.numlock ^= 1; 3969 } 3970 3971 char* 3972 kmap(xkb_keysym_t k, uint state) 3973 { 3974 Key *kp; 3975 int i; 3976 3977 /* Check for mapped keys out of X11 function keys. */ 3978 for (i = 0; i < LEN(mappedkeys); i++) { 3979 if (mappedkeys[i] == k) 3980 break; 3981 } 3982 if (i == LEN(mappedkeys)) { 3983 if ((k & 0xFFFF) < 0xFD00) 3984 return NULL; 3985 } 3986 3987 for (kp = key; kp < key + LEN(key); kp++) { 3988 if (kp->k != k) 3989 continue; 3990 3991 if (!match(kp->mask, state)) 3992 continue; 3993 3994 if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) 3995 continue; 3996 if (term.numlock && kp->appkey == 2) 3997 continue; 3998 3999 if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) 4000 continue; 4001 4002 if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) 4003 continue; 4004 4005 return kp->s; 4006 } 4007 4008 return NULL; 4009 } 4010 4011 void 4012 cresize(int width, int height) 4013 { 4014 int col, row; 4015 4016 if (width != 0) 4017 wl.w = width; 4018 if (height != 0) 4019 wl.h = height; 4020 4021 col = (wl.w - 2 * borderpx) / wl.cw; 4022 row = (wl.h - 2 * borderpx) / wl.ch; 4023 4024 tresize(col, row); 4025 wlresize(col, row); 4026 } 4027 4028 void 4029 regglobal(void *data, struct wl_registry *registry, uint32_t name, 4030 const char *interface, uint32_t version) 4031 { 4032 if (strcmp(interface, "wl_compositor") == 0) { 4033 wl.cmp = wl_registry_bind(registry, name, 4034 &wl_compositor_interface, 3); 4035 } else if (strcmp(interface, "xdg_wm_base") == 0) { 4036 wl.wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); 4037 xdg_wm_base_add_listener(wl.wm, &wmlistener, NULL); 4038 } else if (strcmp(interface, "wl_shm") == 0) { 4039 wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 4040 } else if (strcmp(interface, "wl_seat") == 0) { 4041 wl.seat = wl_registry_bind(registry, name, 4042 &wl_seat_interface, 4); 4043 } else if (strcmp(interface, "wl_data_device_manager") == 0) { 4044 wl.datadevmanager = wl_registry_bind(registry, name, 4045 &wl_data_device_manager_interface, 1); 4046 } else if (strcmp(interface, "wl_output") == 0) { 4047 /* bind to outputs so we can get surface enter events */ 4048 wl_registry_bind(registry, name, &wl_output_interface, 2); 4049 } else if (strcmp(interface, "zwp_text_input_manager_v3") == 0) { 4050 /* bind to outputs so we can get surface enter events */ 4051 wl.timanager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); 4052 } 4053 } 4054 4055 void 4056 regglobalremove(void *data, struct wl_registry *registry, uint32_t name) 4057 { 4058 } 4059 4060 void 4061 surfenter(void *data, struct wl_surface *surface, struct wl_output *output) 4062 { 4063 wl.vis++; 4064 if (!(wl.state & WIN_VISIBLE)) 4065 wl.state |= WIN_VISIBLE; 4066 } 4067 4068 void 4069 surfleave(void *data, struct wl_surface *surface, struct wl_output *output) 4070 { 4071 if (--wl.vis == 0) 4072 wl.state &= ~WIN_VISIBLE; 4073 } 4074 4075 void 4076 framedone(void *data, struct wl_callback *callback, uint32_t msecs) 4077 { 4078 wl_callback_destroy(callback); 4079 wl.framecb = NULL; 4080 if (needdraw && wl.state & WIN_VISIBLE) { 4081 draw(); 4082 } 4083 } 4084 4085 void 4086 kbdkeymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, 4087 uint32_t size) 4088 { 4089 char *string; 4090 4091 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { 4092 close(fd); 4093 return; 4094 } 4095 4096 string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 4097 4098 if (string == MAP_FAILED) { 4099 close(fd); 4100 return; 4101 } 4102 4103 wl.xkb.keymap = xkb_keymap_new_from_string(wl.xkb.ctx, string, 4104 XKB_KEYMAP_FORMAT_TEXT_V1, 0); 4105 munmap(string, size); 4106 close(fd); 4107 wl.xkb.state = xkb_state_new(wl.xkb.keymap); 4108 4109 wl.xkb.ctrl = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_CTRL); 4110 wl.xkb.alt = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_ALT); 4111 wl.xkb.shift = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_SHIFT); 4112 wl.xkb.logo = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_LOGO); 4113 4114 wl.xkb.mods = 0; 4115 } 4116 4117 void 4118 kbdenter(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4119 struct wl_surface *surface, struct wl_array *keys) 4120 { 4121 wl.state |= WIN_FOCUSED; 4122 if (IS_SET(MODE_FOCUS)) 4123 ttywrite("\033[I", 3); 4124 /* need to redraw the cursor */ 4125 needdraw = true; 4126 } 4127 4128 void 4129 kbdleave(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4130 struct wl_surface *surface) 4131 { 4132 /* selection offers are invalidated when we lose keyboard focus */ 4133 wl.seloffer = NULL; 4134 wl.state &= ~WIN_FOCUSED; 4135 if (IS_SET(MODE_FOCUS)) 4136 ttywrite("\033[O", 3); 4137 /* need to redraw the cursor */ 4138 needdraw = true; 4139 /* disable key repeat */ 4140 repeat.len = 0; 4141 } 4142 4143 void 4144 kbdkey(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, 4145 uint32_t key, uint32_t state) 4146 { 4147 xkb_keysym_t ksym; 4148 char buf[32], *str; 4149 int len; 4150 Rune c; 4151 Shortcut *bp; 4152 4153 if (IS_SET(MODE_KBDLOCK)) 4154 return; 4155 4156 if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { 4157 if (repeat.key == key) 4158 repeat.len = 0; 4159 return; 4160 } 4161 4162 ksym = xkb_state_key_get_one_sym(wl.xkb.state, key + 8); 4163 len = xkb_keysym_to_utf8(ksym, buf, sizeof buf); 4164 if (len > 0) 4165 --len; 4166 4167 /* 1. shortcuts */ 4168 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { 4169 if (ksym == bp->keysym && match(bp->mod, wl.xkb.mods)) { 4170 bp->func(&(bp->arg)); 4171 return; 4172 } 4173 } 4174 4175 /* 2. custom keys from config.h */ 4176 if ((str = kmap(ksym, wl.xkb.mods))) { 4177 len = strlen(str); 4178 goto send; 4179 } 4180 4181 /* 3. composed string from input method */ 4182 if (len == 0) 4183 return; 4184 if (len == 1 && wl.xkb.mods & MOD_MASK_ALT) { 4185 if (IS_SET(MODE_8BIT)) { 4186 if (*buf < 0177) { 4187 c = *buf | 0x80; 4188 len = utf8encode(c, buf); 4189 } 4190 } else { 4191 buf[1] = buf[0]; 4192 buf[0] = '\033'; 4193 len = 2; 4194 } 4195 } 4196 /* convert character to control character */ 4197 else if (len == 1 && wl.xkb.mods & MOD_MASK_CTRL) { 4198 if ((*buf >= '@' && *buf < '\177') || *buf == ' ') 4199 *buf &= 0x1F; 4200 else if (*buf == '2') *buf = '\000'; 4201 else if (*buf >= '3' && *buf <= '7') 4202 *buf -= ('3' - '\033'); 4203 else if (*buf == '8') *buf = '\177'; 4204 else if (*buf == '/') *buf = '_' & 0x1F; 4205 } 4206 4207 str = buf; 4208 4209 send: 4210 memcpy(repeat.str, str, len); 4211 repeat.key = key; 4212 repeat.len = len; 4213 repeat.started = false; 4214 clock_gettime(CLOCK_MONOTONIC, &repeat.last); 4215 ttysend(str, len); 4216 } 4217 4218 void 4219 kbdmodifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4220 uint32_t dep, uint32_t lat, uint32_t lck, uint32_t group) 4221 { 4222 xkb_mod_mask_t mod_mask; 4223 4224 xkb_state_update_mask(wl.xkb.state, dep, lat, lck, group, 0, 0); 4225 4226 mod_mask = xkb_state_serialize_mods(wl.xkb.state, XKB_STATE_MODS_EFFECTIVE); 4227 wl.xkb.mods = 0; 4228 4229 if (mod_mask & (1 << wl.xkb.ctrl)) 4230 wl.xkb.mods |= MOD_MASK_CTRL; 4231 if (mod_mask & (1 << wl.xkb.alt)) 4232 wl.xkb.mods |= MOD_MASK_ALT; 4233 if (mod_mask & (1 << wl.xkb.shift)) 4234 wl.xkb.mods |= MOD_MASK_SHIFT; 4235 if (mod_mask & (1 << wl.xkb.logo)) 4236 wl.xkb.mods |= MOD_MASK_LOGO; 4237 } 4238 4239 void 4240 kbdrepeatinfo(void *data, struct wl_keyboard *keyboard, int32_t rate, 4241 int32_t delay) 4242 { 4243 keyrepeatdelay = delay; 4244 keyrepeatinterval = 1000 / rate; 4245 } 4246 4247 void 4248 ptrenter(void *data, struct wl_pointer *pointer, uint32_t serial, 4249 struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) 4250 { 4251 struct wl_cursor_image *img = cursor.cursor->images[0]; 4252 struct wl_buffer *buffer; 4253 4254 wl_pointer_set_cursor(pointer, serial, cursor.surface, 4255 img->hotspot_x, img->hotspot_y); 4256 buffer = wl_cursor_image_get_buffer(img); 4257 wl_surface_attach(cursor.surface, buffer, 0, 0); 4258 wl_surface_damage(cursor.surface, 0, 0, img->width, img->height); 4259 wl_surface_commit(cursor.surface); 4260 } 4261 4262 void 4263 ptrleave(void *data, struct wl_pointer *pointer, uint32_t serial, 4264 struct wl_surface *surface) 4265 { 4266 } 4267 4268 void 4269 ptrmotion(void *data, struct wl_pointer * pointer, uint32_t serial, 4270 wl_fixed_t x, wl_fixed_t y) 4271 { 4272 int oldey, oldex, oldsby, oldsey; 4273 4274 if (IS_SET(MODE_MOUSE)) { 4275 wlmousereportmotion(x, y); 4276 return; 4277 } 4278 4279 wl.px = wl_fixed_to_int(x); 4280 wl.py = wl_fixed_to_int(y); 4281 4282 if (!sel.mode) 4283 return; 4284 4285 sel.mode = SEL_READY; 4286 oldey = sel.oe.y; 4287 oldex = sel.oe.x; 4288 oldsby = sel.nb.y; 4289 oldsey = sel.ne.y; 4290 getbuttoninfo(); 4291 4292 if (oldey != sel.oe.y || oldex != sel.oe.x) 4293 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); 4294 } 4295 4296 void 4297 ptrbutton(void * data, struct wl_pointer * pointer, uint32_t serial, 4298 uint32_t time, uint32_t button, uint32_t state) 4299 { 4300 MouseShortcut *ms; 4301 4302 if (IS_SET(MODE_MOUSE) && !(wl.xkb.mods & forceselmod)) { 4303 wlmousereportbutton(button, state); 4304 return; 4305 } 4306 4307 switch (state) { 4308 case WL_POINTER_BUTTON_STATE_RELEASED: 4309 switch (button) { 4310 case BTN_MIDDLE: 4311 selpaste(NULL); 4312 break; 4313 case BTN_LEFT: 4314 if (sel.mode == SEL_READY) { 4315 getbuttoninfo(); 4316 selcopy(serial); 4317 } else { 4318 selclear(); 4319 } 4320 sel.mode = SEL_IDLE; 4321 tsetdirt(sel.nb.y, sel.ne.y); 4322 break; 4323 case BTN_RIGHT: 4324 plumb(sel.primary); 4325 break; 4326 } 4327 break; 4328 4329 case WL_POINTER_BUTTON_STATE_PRESSED: 4330 for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { 4331 if (button == ms->b && match(ms->mask, wl.xkb.mods)) { 4332 ttysend(ms->s, strlen(ms->s)); 4333 return; 4334 } 4335 } 4336 4337 if (button == BTN_LEFT) { 4338 /* Clear previous selection, logically and visually. */ 4339 selclear(); 4340 sel.mode = SEL_EMPTY; 4341 sel.type = SEL_REGULAR; 4342 sel.oe.x = sel.ob.x = x2col(wl.px); 4343 sel.oe.y = sel.ob.y = y2row(wl.py); 4344 4345 /* 4346 * If the user clicks below predefined timeouts 4347 * specific snapping behaviour is exposed. 4348 */ 4349 if (time - sel.tclick2 <= tripleclicktimeout) { 4350 sel.snap = SNAP_LINE; 4351 } else if (time - sel.tclick1 <= doubleclicktimeout) { 4352 sel.snap = SNAP_WORD; 4353 } else { 4354 sel.snap = 0; 4355 } 4356 selnormalize(); 4357 4358 if (sel.snap != 0) 4359 sel.mode = SEL_READY; 4360 tsetdirt(sel.nb.y, sel.ne.y); 4361 sel.tclick2 = sel.tclick1; 4362 sel.tclick1 = time; 4363 } 4364 break; 4365 } 4366 } 4367 4368 void 4369 ptraxis(void * data, struct wl_pointer * pointer, uint32_t time, uint32_t axis, 4370 wl_fixed_t value) 4371 { 4372 Axiskey *ak; 4373 int dir = value > 0 ? +1 : -1; 4374 4375 if (IS_SET(MODE_MOUSE) && !(wl.xkb.mods & forceselmod)) { 4376 wlmousereportaxis(axis, value); 4377 return; 4378 } 4379 4380 for (ak = ashortcuts; ak < ashortcuts + LEN(ashortcuts); ak++) { 4381 if (axis == ak->axis && dir == ak->dir 4382 && match(ak->mask, wl.xkb.mods)) { 4383 ttysend(ak->s, strlen(ak->s)); 4384 return; 4385 } 4386 } 4387 } 4388 4389 void 4390 tienter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) 4391 { 4392 zwp_text_input_v3_enable(text_input); 4393 zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_LOWERCASE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); 4394 wl.textserial++; 4395 zwp_text_input_v3_commit(text_input); 4396 } 4397 4398 void 4399 tileave(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) 4400 { 4401 zwp_text_input_v3_disable(text_input); 4402 } 4403 4404 void 4405 tipreedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end) 4406 { 4407 } 4408 4409 void 4410 ticommit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text) 4411 { 4412 strcpy(wl.tipending.commitbuf, text); 4413 } 4414 4415 void 4416 tidelete_surrounding_text(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t before_length, uint32_t after_length) 4417 { 4418 wl.tipending.delbefore = before_length; 4419 wl.tipending.delafter = after_length; 4420 } 4421 4422 void 4423 tidone(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t serial) 4424 { 4425 wl.tistate.delbefore = wl.tipending.delbefore; 4426 wl.tistate.delafter = wl.tipending.delafter; 4427 strcpy(wl.tistate.commitbuf, wl.tipending.commitbuf); 4428 wl.tipending.delbefore = 0; 4429 wl.tipending.delafter = 0; 4430 wl.tipending.commitbuf[0] = 0; 4431 4432 if (serial != wl.textserial) 4433 return; 4434 4435 /* 2. Delete requested surrounding text */ 4436 for (int i = 0; i < wl.tistate.delbefore; i++) 4437 ttysend(BACKSPACE, sizeof(BACKSPACE)); 4438 4439 /* 3. Insert commit string with the cursor at its end */ 4440 ttysend(wl.tistate.commitbuf, strlen(wl.tistate.commitbuf)); 4441 4442 zwp_text_input_v3_commit(wl.textinput); 4443 wl.textserial++; 4444 } 4445 4446 void 4447 wmping(void *data, struct xdg_wm_base *wm, uint32_t serial) 4448 { 4449 xdg_wm_base_pong(wm, serial); 4450 } 4451 4452 void 4453 xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial) 4454 { 4455 xdg_surface_ack_configure(surf, serial); 4456 } 4457 4458 void 4459 toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, 4460 struct wl_array *states) 4461 { 4462 if (w == wl.w && h == wl.h) 4463 return; 4464 cresize(w, h); 4465 if (wl.configured) 4466 ttyresize(); 4467 else 4468 wl.configured = true; 4469 } 4470 4471 void 4472 toplevelclose(void *data, struct xdg_toplevel *toplevel) 4473 { 4474 if (pid) { 4475 /* Send SIGHUP to shell */ 4476 kill(pid, SIGHUP); 4477 } 4478 exit(0); 4479 } 4480 4481 void 4482 datadevoffer(void *data, struct wl_data_device *datadev, 4483 struct wl_data_offer *offer) 4484 { 4485 wl_data_offer_add_listener(offer, &dataofferlistener, NULL); 4486 } 4487 4488 void 4489 datadeventer(void *data, struct wl_data_device *datadev, uint32_t serial, 4490 struct wl_surface *surf, wl_fixed_t x, wl_fixed_t y, 4491 struct wl_data_offer *offer) 4492 { 4493 } 4494 4495 void 4496 datadevleave(void *data, struct wl_data_device *datadev) 4497 { 4498 } 4499 4500 void 4501 datadevmotion(void *data, struct wl_data_device *datadev, uint32_t time, 4502 wl_fixed_t x, wl_fixed_t y) 4503 { 4504 } 4505 4506 void 4507 datadevdrop(void *data, struct wl_data_device *datadev) 4508 { 4509 } 4510 4511 void 4512 datadevselection(void *data, struct wl_data_device *datadev, 4513 struct wl_data_offer *offer) 4514 { 4515 if (offer && (uintptr_t) wl_data_offer_get_user_data(offer) == 1) 4516 wl.seloffer = offer; 4517 else 4518 wl.seloffer = NULL; 4519 } 4520 4521 void 4522 dataofferoffer(void *data, struct wl_data_offer *offer, const char *mimetype) 4523 { 4524 /* mark the offer as usable if it supports plain text */ 4525 if (strncmp(mimetype, "text/plain", 10) == 0) 4526 wl_data_offer_set_user_data(offer, (void *)(uintptr_t) 1); 4527 } 4528 4529 void 4530 datasrctarget(void *data, struct wl_data_source *source, const char *mimetype) 4531 { 4532 } 4533 4534 void 4535 datasrcsend(void *data, struct wl_data_source *source, const char *mimetype, 4536 int32_t fd) 4537 { 4538 char *buf = sel.primary; 4539 int len = strlen(sel.primary); 4540 ssize_t ret; 4541 while ((ret = write(fd, buf, MIN(len, BUFSIZ))) > 0) { 4542 len -= ret; 4543 buf += ret; 4544 } 4545 close(fd); 4546 } 4547 4548 void 4549 datasrccancelled(void *data, struct wl_data_source *source) 4550 { 4551 if (sel.source == source) { 4552 sel.source = NULL; 4553 selclear(); 4554 } 4555 wl_data_source_destroy(source); 4556 } 4557 4558 void 4559 run(void) 4560 { 4561 fd_set rfd; 4562 int wlfd = wl_display_get_fd(wl.dpy), blinkset = 0; 4563 struct timespec drawtimeout, *tv = NULL, now, last, lastblink; 4564 ulong msecs; 4565 4566 ttynew(); 4567 4568 /* Look for initial configure. */ 4569 wl_display_roundtrip(wl.dpy); 4570 if (!wl.configured) { 4571 cresize(wl.w, wl.h); 4572 ttyresize(); 4573 } 4574 draw(); 4575 4576 clock_gettime(CLOCK_MONOTONIC, &last); 4577 lastblink = last; 4578 4579 for (;;) { 4580 FD_ZERO(&rfd); 4581 FD_SET(cmdfd, &rfd); 4582 FD_SET(wlfd, &rfd); 4583 4584 if (pselect(MAX(wlfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { 4585 if (errno == EINTR) 4586 continue; 4587 die("select failed: %s\n", strerror(errno)); 4588 } 4589 4590 if (FD_ISSET(cmdfd, &rfd)) { 4591 ttyread(); 4592 if (blinktimeout) { 4593 blinkset = tattrset(ATTR_BLINK); 4594 if (!blinkset) 4595 MODBIT(term.mode, 0, MODE_BLINK); 4596 } 4597 } 4598 4599 if (FD_ISSET(wlfd, &rfd)) { 4600 if (wl_display_dispatch(wl.dpy) == -1) 4601 die("Connection error\n"); 4602 } 4603 4604 clock_gettime(CLOCK_MONOTONIC, &now); 4605 msecs = -1; 4606 4607 if (blinkset && blinktimeout) { 4608 if (TIMEDIFF(now, lastblink) >= blinktimeout) { 4609 tsetdirtattr(ATTR_BLINK); 4610 term.mode ^= MODE_BLINK; 4611 lastblink = now; 4612 } else { 4613 msecs = MIN(msecs, blinktimeout - \ 4614 TIMEDIFF(now, lastblink)); 4615 } 4616 } 4617 if (repeat.len > 0) { 4618 if (TIMEDIFF(now, repeat.last) >= \ 4619 (repeat.started ? keyrepeatinterval : \ 4620 keyrepeatdelay)) { 4621 repeat.started = true; 4622 repeat.last = now; 4623 ttysend(repeat.str, repeat.len); 4624 } else { 4625 msecs = MIN(msecs, (repeat.started ? \ 4626 keyrepeatinterval : keyrepeatdelay) - \ 4627 TIMEDIFF(now, repeat.last)); 4628 } 4629 } 4630 4631 if (needdraw && wl.state & WIN_VISIBLE) { 4632 if (!wl.framecb) { 4633 draw(); 4634 } 4635 } 4636 4637 if (msecs == -1) { 4638 tv = NULL; 4639 } else { 4640 drawtimeout.tv_nsec = 1E6 * msecs; 4641 drawtimeout.tv_sec = 0; 4642 tv = &drawtimeout; 4643 } 4644 4645 wl_display_dispatch_pending(wl.dpy); 4646 wl_display_flush(wl.dpy); 4647 } 4648 } 4649 4650 void 4651 usage(void) 4652 { 4653 die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 4654 " [-n name] [-o file]\n" 4655 " [-T title] [-t title] [-w windowid]" 4656 " [[-e] command [args ...]]\n" 4657 " %s [-aiv] [-c class] [-f font] [-g geometry]" 4658 " [-n name] [-o file]\n" 4659 " [-T title] [-t title] [-w windowid] -l line" 4660 " [stty_args ...]\n", argv0, argv0); 4661 } 4662 4663 int 4664 main(int argc, char *argv[]) 4665 { 4666 wl.cursor = cursorshape; 4667 4668 ARGBEGIN { 4669 case 'a': 4670 allowaltscreen = 0; 4671 break; 4672 case 'c': 4673 opt_class = EARGF(usage()); 4674 break; 4675 case 'e': 4676 if (argc > 0) 4677 --argc, ++argv; 4678 goto run; 4679 case 'f': 4680 opt_font = EARGF(usage()); 4681 break; 4682 case 'o': 4683 opt_io = EARGF(usage()); 4684 break; 4685 case 'l': 4686 opt_line = EARGF(usage()); 4687 break; 4688 case 'n': 4689 opt_name = EARGF(usage()); 4690 break; 4691 case 't': 4692 case 'T': 4693 opt_title = EARGF(usage()); 4694 break; 4695 case 'w': 4696 opt_embed = EARGF(usage()); 4697 break; 4698 case 'v': 4699 die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); 4700 break; 4701 default: 4702 usage(); 4703 } ARGEND; 4704 4705 run: 4706 plumbinit(); 4707 if (argc > 0) { 4708 /* eat all remaining arguments */ 4709 opt_cmd = argv; 4710 if (!opt_title && !opt_line) 4711 opt_title = basename(xstrdup(argv[0])); 4712 } 4713 setlocale(LC_CTYPE, ""); 4714 tnew(MAX(cols, 1), MAX(rows, 1)); 4715 wlinit(); 4716 selinit(); 4717 run(); 4718 4719 return 0; 4720 } 4721