From 4c59b1645735a74d215f9d0aafbcecff2aa57d47 Mon Sep 17 00:00:00 2001 From: kvj Date: Tue, 24 Oct 2023 12:44:14 +0200 Subject: [PATCH] Alpha patch support --- FAQ | 3 + Makefile | 28 +- README | 34 + README.md | 308 +- config.def.h | 825 ++++-- config.h | 823 ++++-- config.mk | 32 +- hb.c | 108 + hb.h | 14 + patch/alpha.c | 30 + patch/alpha.h | 5 + patch/background_image_x.c | 106 + patch/background_image_x.h | 6 + patch/boxdraw.c | 192 ++ patch/boxdraw.h | 214 ++ patch/copyurl.c | 180 ++ patch/copyurl.h | 5 + patch/externalpipe.c | 76 + patch/externalpipe.h | 4 + patch/fixkeyboardinput.c | 811 ++++++ patch/font2.c | 104 + patch/font2.h | 2 + patch/fullscreen_x.c | 17 + patch/fullscreen_x.h | 1 + patch/invert.c | 21 + patch/invert.h | 1 + patch/iso14755.c | 21 + patch/iso14755.h | 6 + patch/keyboardselect_st.c | 240 ++ patch/keyboardselect_st.h | 2 + patch/keyboardselect_x.c | 7 + patch/keyboardselect_x.h | 2 + patch/netwmicon.h | 686 +++++ patch/newterm.c | 30 + patch/newterm.h | 2 + patch/normalMode.c | 284 ++ patch/normalMode.h | 8 + patch/opencopied.c | 19 + patch/opencopied.h | 1 + patch/openurlonclick.c | 141 + patch/openurlonclick.h | 8 + patch/rightclicktoplumb_st.c | 19 + patch/rightclicktoplumb_st.h | 1 + patch/rightclicktoplumb_x.c | 24 + patch/rightclicktoplumb_x.h | 1 + patch/scrollback.c | 55 + patch/scrollback.h | 17 + patch/sixel_st.c | 42 + patch/sixel_st.h | 2 + patch/sixel_x.c | 14 + patch/st_embedder_x.c | 50 + patch/st_embedder_x.h | 7 + patch/st_include.c | 31 + patch/st_include.h | 34 + patch/sync.c | 31 + patch/sync.h | 7 + patch/utils.h | 23 + patch/x_include.c | 43 + patch/x_include.h | 40 + patch/xresources.c | 82 + patch/xresources.h | 17 + patches.def.h | 454 +++ patches.h | 454 +++ sixel.c | 616 ++++ sixel.h | 58 + sixel_hls.c | 115 + sixel_hls.h | 7 + st | Bin 122376 -> 143800 bytes st.c | 1250 +++++++-- st.desktop | 12 +- st.h | 263 +- st.info | 17 +- st.o | Bin 81560 -> 83096 bytes win.h | 18 +- x.c | 5125 ++++++++++++++++++++++------------ x.o | Bin 90056 -> 118848 bytes 76 files changed, 11723 insertions(+), 2613 deletions(-) create mode 100644 README create mode 100644 hb.c create mode 100644 hb.h create mode 100644 patch/alpha.c create mode 100644 patch/alpha.h create mode 100644 patch/background_image_x.c create mode 100644 patch/background_image_x.h create mode 100644 patch/boxdraw.c create mode 100644 patch/boxdraw.h create mode 100644 patch/copyurl.c create mode 100644 patch/copyurl.h create mode 100644 patch/externalpipe.c create mode 100644 patch/externalpipe.h create mode 100644 patch/fixkeyboardinput.c create mode 100644 patch/font2.c create mode 100644 patch/font2.h create mode 100644 patch/fullscreen_x.c create mode 100644 patch/fullscreen_x.h create mode 100644 patch/invert.c create mode 100644 patch/invert.h create mode 100644 patch/iso14755.c create mode 100644 patch/iso14755.h create mode 100644 patch/keyboardselect_st.c create mode 100644 patch/keyboardselect_st.h create mode 100644 patch/keyboardselect_x.c create mode 100644 patch/keyboardselect_x.h create mode 100644 patch/netwmicon.h create mode 100644 patch/newterm.c create mode 100644 patch/newterm.h create mode 100644 patch/normalMode.c create mode 100644 patch/normalMode.h create mode 100644 patch/opencopied.c create mode 100644 patch/opencopied.h create mode 100644 patch/openurlonclick.c create mode 100644 patch/openurlonclick.h create mode 100644 patch/rightclicktoplumb_st.c create mode 100644 patch/rightclicktoplumb_st.h create mode 100644 patch/rightclicktoplumb_x.c create mode 100644 patch/rightclicktoplumb_x.h create mode 100644 patch/scrollback.c create mode 100644 patch/scrollback.h create mode 100644 patch/sixel_st.c create mode 100644 patch/sixel_st.h create mode 100644 patch/sixel_x.c create mode 100644 patch/st_embedder_x.c create mode 100644 patch/st_embedder_x.h create mode 100644 patch/st_include.c create mode 100644 patch/st_include.h create mode 100644 patch/sync.c create mode 100644 patch/sync.h create mode 100644 patch/utils.h create mode 100644 patch/x_include.c create mode 100644 patch/x_include.h create mode 100644 patch/xresources.c create mode 100644 patch/xresources.h create mode 100644 patches.def.h create mode 100644 patches.h create mode 100644 sixel.c create mode 100644 sixel.h create mode 100644 sixel_hls.c create mode 100644 sixel_hls.h diff --git a/FAQ b/FAQ index 969b195..b4d0b8a 100644 --- a/FAQ +++ b/FAQ @@ -248,3 +248,6 @@ fonts: Please don't bother reporting this bug to st, but notify the upstream Xft developers about fixing this bug. + +As of 2022-09-05 this now seems to be finally fixed in libXft 2.3.5: +https://gitlab.freedesktop.org/xorg/lib/libxft/-/blob/libXft-2.3.5/NEWS \ No newline at end of file diff --git a/Makefile b/Makefile index 3fdf13a..2b3859a 100644 --- a/Makefile +++ b/Makefile @@ -4,28 +4,24 @@ include config.mk -SRC = st.c x.c boxdraw.c +SRC = st.c x.c $(LIGATURES_C) $(SIXEL_C) OBJ = $(SRC:.c=.o) -all: options st - -options: - @echo st build options: - @echo "CFLAGS = $(STCFLAGS)" - @echo "LDFLAGS = $(STLDFLAGS)" - @echo "CC = $(CC)" +all: st config.h: cp config.def.h config.h +patches.h: + cp patches.def.h patches.h + .c.o: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h config.h st.h win.h -boxdraw.o: config.h st.h boxdraw_data.h +x.o: arg.h config.h st.h win.h $(LIGATURES_H) -$(OBJ): config.h config.mk +$(OBJ): config.h config.mk patches.h st: $(OBJ) $(CC) -o $@ $(OBJ) $(STLDFLAGS) @@ -36,7 +32,7 @@ clean: dist: clean mkdir -p st-$(VERSION) cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ - config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + config.def.h st.info st.1 arg.h st.h win.h $(LIGATURES_H) $(SRC)\ st-$(VERSION) tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz rm -rf st-$(VERSION) @@ -49,13 +45,13 @@ install: st sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 tic -sx st.info + mkdir -p $(DESTDIR)$(PREFIX)/share/applications # desktop-entry patch + test -f ${DESTDIR}${PREFIX}/share/applications/st.desktop || cp -n st.desktop $(DESTDIR)$(PREFIX)/share/applications # desktop-entry patch @echo Please see the README file regarding the terminfo entry of st. - mkdir -p $(DESTDIR)$(APPPREFIX) - cp -f st.desktop $(DESTDIR)$(APPPREFIX) uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/st - rm -f $(DESTDIR)$(APPPREFIX)/st.desktop rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + rm -f $(DESTDIR)$(PREFIX)/share/applications/st.desktop # desktop-entry patch -.PHONY: all options clean dist install uninstall +.PHONY: all clean dist install uninstall diff --git a/README b/README new file mode 100644 index 0000000..6a846ed --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +st - simple terminal +-------------------- +st is a simple terminal emulator for X which sucks less. + + +Requirements +------------ +In order to build st you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (st is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install st (if +necessary as root): + + make clean install + + +Running st +---------- +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + + tic -sx st.info + +See the man page for additional details. + +Credits +------- +Based on Aurélien APTEL bt source code. + diff --git a/README.md b/README.md index 1a65ff3..dccaa15 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,307 @@ -# st +Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this st 0.9 (9846a56, 2023-10-07) project has a different take on st patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. -Fork of st (https://st.suckless.org) and best (https://github.com/chadcat7/best) \ No newline at end of file +For example to include the `alpha` patch then you would only need to flip this setting from 0 to 1 in [patches.h](https://github.com/bakkeby/st-flexipatch/blob/master/patches.def.h): +```c +#define ALPHA_PATCH 1 +``` + +Once you have found out what works for you and what doesn't then you should be in a better position to choose patches should you want to start patching from scratch. + +Alternatively if you have found the patches you want, but don't want the rest of the flexipatch entanglement on your plate then you may want to have a look at [flexipatch-finalizer](https://github.com/bakkeby/flexipatch-finalizer); a custom pre-processor tool that removes all the unused flexipatch code leaving you with a build that contains the patches you selected. + +Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the st terminal, how to install it and how it works. + +--- + +### Changelog: + +2022-10-24 - Added the fullscreen patch + +2022-08-28 - Added the use XftFontMatch patch + +2022-08-24 - Added the no window decorations patch + +2022-04-11 - Added the background image reload patch + +2022-03-10 - Added the background image patch + +2022-02-24 - Upgraded to st 0.8.5 e823e23, 2022-02-17 - removing osc_10_11_12_2 patch as no longer relevant + +2021-08-18 - Added the CSI 22 & 23 patch + +2021-07-26 - Added columns patch + +2021-07-07 - Added sixel scrollback and the openurlonclick patch + +2021-06-09 - Added the hide terminal cursor patch + +2021-05-16 - Added swapmouse patch + +2021-05-11 - Added default cursor patch + +2021-05-10 - Upgrade to 46b02f, 2021-03-28 + +2021-05-09 - Added the sync, alpha-focus-hightlight and vim browse patches + +2021-05-08 - Added blinking cursor, delkey, undercurl,universcroll, desktopentry, netwmicon and osc_10_11_12_2 patches + +2021-05-07 - Added xresources reload patch + +2021-04-21 - Added (temporary?) hack for Variable Fonts (VT) support + +2021-03-10 - Added sixel support + +2021-02-26 - Added the dynamic cursor color patch + +2021-02-15 - Added the alpha gradient patch + +2020-11-14 - Added the wide glyphs patch + +2020-10-23 - Added the monochrome patch + +2020-08-08 - Re-added the visualbell patch + +2020-06-26 - Added the single drawable buffer patch as per the FAQ in order to get w3m images to display + +2020-06-25 - Upgrade to 0.8.4 (367803, 2020-06-19) + +2020-06-14 - Added w3m patch + +2020-06-10 - Upgrade to 249ef9, 2020-06-01 + +2020-06-05 - Added the ligatures patch + +2020-05-20 - Upgrade to 222876, 2020-05-09, and removed visualbell 1, 2, 3 patches and force redraw after keypress due to incompatibility. Refer to tag [371878](https://github.com/bakkeby/st-flexipatch/tree/371878) if you want to try these out. + +2020-04-20 - Upgrade to c279f5, 2020-04-19, and added the force redraw on pselect after key is pressed patch and the externalpipein patch + +2020-03-29 - Added invert and workingdir patches + +2020-03-24 - Upgraded to latest (master) of st (commit 51e19ea11dd42eefed1ca136ee3f6be975f618b1 at the time of writing). Custom changes to make the altscreen mouse scollback patch working. + +2020-03-21 - Added font2 patch + +2020-01-07 - Added st embedder patch + +2019-10-16 - Introduced [flexipatch-finalizer](https://github.com/bakkeby/flexipatch-finalizer) + +2019-09-17 - Added relativeborder, fix-keyboard-input, iso14755, visualbell, right-click-to-plumb, boxdraw and keyboard-select patches + +2019-09-16 - Added alpha, anysize, bold-is-not-bright, clipboard, copyurl, disable-fonts, externalpipe, fixime, hidecursor, newterm, open-copied-url, vertcenter, scrollback, spoiler, themed cursor and xresources patches + +### Patches included: + + - [alpha](https://st.suckless.org/patches/alpha/) + - adds transparency for the terminal + + - [alpha-focus-highlight](https://st.suckless.org/patches/alpha_focus_highlight/) + - allows the user to specify two distinct opacity values or background colors in order to + easily differentiate between focused and unfocused terminal windows + + - [anysize](https://st.suckless.org/patches/anysize/) + - allows st to reize to any pixel size rather than snapping to character width / height + + - [~anysize\_nobar~](https://github.com/connor-brooks/st-anysize-nobar) + - ~a patch that aims to prevent black bars being drawn on the edges of st terminals using the + anysize patch~ + + - [background-image](https://st.suckless.org/patches/background_image/) + - draws a background image in farbfeld format in place of the defaultbg color allowing for + pseudo transparency + + - background-image-reload + - allows the background image to be reloaded similar to xresources using USR1 signals + + - [blinking-cursor](https://st.suckless.org/patches/blinking_cursor/) + - allows the use of a blinking cursor + + - [bold-is-not-bright](https://st.suckless.org/patches/bold-is-not-bright/) + - by default bold text is rendered with a bold font in the bright variant of the current color + - this patch makes bold text rendered simply as bold, leaving the color unaffected + + - [boxdraw](https://st.suckless.org/patches/boxdraw/) + - adds dustom rendering of lines/blocks/braille characters for gapless alignment + + - [clipboard](https://st.suckless.org/patches/clipboard/) + - by default st only sets PRIMARY on selection + - this patch makes st set CLIPBOARD on selection + + - [columns](https://github.com/bakkeby/st-flexipatch/issues/34) + - allows st to be resized without cutting off text when the terminal window is made larger again + - text does not wrap when the terminal window is made smaller + + - [copyurl](https://st.suckless.org/patches/copyurl/) + - this patch allows you to select and copy the last URL displayed with Mod+l + - multiple invocations cycle through the available URLs + + - [csi\_23\_23](https://st.suckless.org/patches/csi_22_23/) + - adds support for CSI escape sequences 22 and 23, which save and restores the window title + (for instance nvim does this when opening and closing) + + - default-cursor + - minor change allowing escape sequences like `\e[ q` or `\e[0 q` to set the cursor back to default configuration instead of a blinking block + - while many terminals do this the behaviour is not according to the specification + + - [delkey](https://st.suckless.org/patches/delkey/) + - return BS on pressing backspace and DEL on pressing the delete key + + - [desktopentry](https://st.suckless.org/patches/desktopentry/) + - adds a desktop entry for st so that it can be displayed with an icon when using a graphical launcher + - this patch only applies to the Makefile and is enabled by default, remove if not needed + + - [disable-fonts](https://st.suckless.org/patches/disable_bold_italic_fonts/) + - this patch adds the option of disabling bold/italic/roman fonts globally + + - [dynamic-cursor-color](https://st.suckless.org/patches/dynamic-cursor-color/) + - this patch makes the cursor color the inverse of the current cell color + + - [externalpipe](https://st.suckless.org/patches/externalpipe/) + - this patch allows for reading and writing st's screen through a pipe, e.g. to pass info to dmenu + + - [externalpipein](https://lists.suckless.org/hackers/2004/17218.html) + - this patch prevents the reset of the signal handler set on SIGCHILD, when the forked process that executes the external process exits + - it adds the externalpipein function to redirect the standard output of the external command to the slave size of the pty, that is, as if the external program had been manually executed on the terminal + - this can be used to send desired escape sequences to the terminal with a shortcut (e.g. to change colors) + + - [~fixime~](https://st.suckless.org/patches/fix_ime/) + - adds better Input Method Editor (IME) support + - (included in the base as per [35f7db](https://git.suckless.org/st/commit/e85b6b64660214121164ea97fb098eaa4935f7db.html)) + + - [fix-keyboard-input](https://st.suckless.org/patches/fix_keyboard_input/) + - allows cli applications to use all the fancy key combinations that are available to GUI applications + + - [font2](https://st.suckless.org/patches/font2/) + - allows you to add a spare font besides the default + + - [~force-redraw-after-keypress~](https://lists.suckless.org/hackers/2004/17221.html) + - ~this patch forces the terminal to check for new data on the tty on keypress with the aim of reducing input latency~ + + - [fullscreen](https://st.suckless.org/patches/fullscreen/) + - allows the st window to go into fullscreen mode + + - [gradient](https://st.suckless.org/patches/gradient/) + - adds gradient transparency to st + - depends on the alpha patch + + - [hidecursor](https://st.suckless.org/patches/hidecursor/) + - hides the X cursor whenever a key is pressed and show it back when the mouse is moved in the terminal window + + - [hide-terminal-cursor](https://www.reddit.com/r/suckless/comments/nvee8h/how_to_hide_cursor_in_st_is_there_a_patch_for_it/) + - hides the terminal cursor when the window loses focus (as opposed to showing a hollow cursor) + + - [invert](https://st.suckless.org/patches/invert/) + - adds a keybinding that lets you invert the current colorscheme of st + - this provides a simple way to temporarily switch to a light colorscheme if you use a dark colorscheme or visa-versa + + - [iso14755](https://st.suckless.org/patches/iso14755/) + - pressing the default binding Ctrl+Shift-i will popup dmenu, asking you to enter a unicode codepoint that will be converted to a glyph and then pushed to st + + - [keyboard-select](https://st.suckless.org/patches/keyboard_select/) + - allows you to select text on the terminal using keyboard shortcuts + + - [ligatures](https://st.suckless.org/patches/ligatures/) + - adds support for drawing ligatures using the Harfbuzz library to transform original text of a single line to a list of glyphs with ligatures included + + - [monochrome](https://www.reddit.com/r/suckless/comments/ixbx6z/how_to_use_black_and_white_only_for_st/) + - makes st ignore terminal color attributes to make for a monochrome look + + - [netwmicon](https://st.suckless.org/patches/netwmicon/) + - this patch sets the \_NET\_WM\_ICON X property with a hardcoded icon for st + + - [newterm](https://st.suckless.org/patches/newterm/) + - allows you to spawn a new st terminal using Ctrl-Shift-Return + - it will have the same CWD (current working directory) as the original st instance + + - [no-window-decorations](https://github.com/bakkeby/patches/wiki/no_window_decorations) + - makes st show without window decorations if the WM supports it + + - [open-copied-url](https://st.suckless.org/patches/open_copied_url/) + - open contents of the clipboard in a user-defined browser + + - [openurlonclick](https://www.reddit.com/r/suckless/comments/cc83om/st_open_url/) + - allows for URLs to be opened directly when you click on them + + - [~osc\_10\_11\_12\_2~](https://st.suckless.org/patches/osc_10_11_12_2/) + - ~this patch adds support for OSC escape sequences 10, 11, and 12 in the way they are~ + ~implemented in most other terminals (e.g libvte, kitty)~ + - ~specifically it differs from~ [~osc_10_11_12~](https://st.suckless.org/patches/osc_10_11_12/) + ~in that it treats the background and foreground colors as distinct from palette colours 01~ + ~and 07 in order to facilitate the use of theme setting scripts like~ + [~theme.sh~](https://github.com/lemnos/theme.sh) ~which expect these colours to be distinct~ + + - [relativeborder](https://st.suckless.org/patches/relativeborder/) + - allows you to specify a border that is relative in size to the width of a cell in the + terminal + + - [right-click-to-plumb](https://st.suckless.org/patches/right_click_to_plumb/) + - allows you to right-click on some selected text to send it to the plumbing program of choice + + - [scrollback](https://st.suckless.org/patches/scrollback/) + - allows you scroll back through terminal output using keyboard shortcuts or mousewheel + + - sixel + - this patch adds SIXEL graphics support + + - st-embedder + - this patch allows clients to embed into the st window and can be useful if you tend to + start X applications from the terminal + - the behavior is similar to Plan 9 where applications can take over windows + + - [spoiler](https://st.suckless.org/patches/spoiler/) + - use inverted defaultbg/fg for selection when bg/fg are the same + + - [swapmouse](https://st.suckless.org/patches/swapmouse/) + - changes the mouse shape to the global default when the running program subscribes for mouse + events, for instance, in programs like ranger and fzf + - it emulates the behaviour shown by vte terminals like termite + + - [sync](https://st.suckless.org/patches/sync/) + - adds synchronized-updates/application-sync support in st + - this has no effect except when an application uses the synchronized-update escape sequences + - with this patch nearly all cursor flicker is eliminated in tmux, and tmux detects it + automatically via terminfo + + - [themed-cursor](https://st.suckless.org/patches/themed_cursor/) + - instead of a default X cursor, use the xterm cursor from your cursor theme + + - [undercurl](https://st.suckless.org/patches/undercurl/) + - adds support for special underlines, e.g. curly / wavy underlines + + - [universcroll](https://st.suckless.org/patches/universcroll/) + - allows mouse scroll without modifier keys for regardless of alt screen using the external + scroll program + + - [use-XftFontMatch](https://git.suckless.org/st/commit/528241aa3835e2f1f052abeeaf891737712955a0.html) + - use XftFontMatch in place of FcFontMatch to allow font to scale with Xft.dpi resource + setting + + - [vertcenter](https://st.suckless.org/patches/vertcenter/) + - vertically center lines in the space available if you have set a larger chscale in config.h + + - [vim-browse](https://st.suckless.org/patches/vim_browse/) + - the vim-browse patch offers the possibility to move through the terminal history-buffer, + search for strings using VIM-like motions, operations and quantifiers + - it overlays the screen with highlighted search results and displays the current operation + / motions / search string in the bottom right corner + + - [visualbell](https://st.suckless.org/patches/visualbell/) + - adds visual indicators for the terminal bell event + + - [w3m](https://st.suckless.org/patches/w3m/) + - adds support for w3m images + + - [wide-glyphs](https://www.reddit.com/r/suckless/comments/jt90ai/update_support_for_proper_glyph_rendering_in_st/) + - adds proper support for wide glyphs, as opposed to rendering smaller or cut glyphs + + - [wide-glyph-spacing](https://github.com/googlefonts/Inconsolata/issues/42#issuecomment-737508890) + - there is a known issue that Google's Variable Fonts (VF) can end up with letter spacing + that is too wide in programs that use Xft, for example Inconsolata v3.000 + - this is intended as a temporary workaround / patch / hack until (if) this is fixed in the + Xft library itself + + - [workingdir](https://st.suckless.org/patches/workingdir/) + - allows user to specify the initial path st should use as the working directory + + - [xresources](https://st.suckless.org/patches/xresources/) + - adds the ability to configure st via Xresources + - during startup, st will read and apply the resources named in the resources[] array in config.h diff --git a/config.def.h b/config.def.h index e03f6a3..93d352e 100644 --- a/config.def.h +++ b/config.def.h @@ -5,12 +5,38 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char *font = - "Iosevka Nerd Font:pixelsize=20:antialias=true:autohint=true"; +static char *font = "Iosevka Nerd Font:pixelsize=20:antialias=true:autohint=true"; +#if FONT2_PATCH +/* Spare fonts */ static char *font2[] = { - "Noto Color Emoji:pixelsize=20:antialias=true:autohint=true", - "Material Design Icons Desktop:pixelsize=19"}; -static int borderpx = 0; +/* "Inconsolata for Powerline:pixelsize=12:antialias=true:autohint=true", */ +/* "Hack Nerd Font Mono:pixelsize=11:antialias=true:autohint=true", */ +}; +#endif // FONT2_PATCH + +#if BACKGROUND_IMAGE_PATCH +/* + * background image + * expects farbfeld format + * pseudo transparency fixes coordinates to the screen origin + */ +static const char *bgfile = "/path/to/image.ff"; +static const int pseudotransparency = 0; +#endif // BACKGROUND_IMAGE_PATCH + +#if RELATIVEBORDER_PATCH +/* borderperc: percentage of cell width to use as a border + * 0 = no border, 100 = border width is same as cell width */ +int borderperc = 20; +#else +static int borderpx = 2; +#endif // RELATIVEBORDER_PATCH + +#if OPENURLONCLICK_PATCH +/* modkey options: ControlMask, ShiftMask or XK_ANY_MOD */ +static uint url_opener_modkey = XK_ANY_MOD; +static char *url_opener = "xdg-open"; +#endif // OPENURLONCLICK_PATCH /* * What program is execed by st depends of these precedence rules: @@ -27,11 +53,15 @@ char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ +#if SIXEL_PATCH +char *vtiden = "\033[?12;4c"; +#else char *vtiden = "\033[?6c"; +#endif /* Kerning / character bounding-box multipliers */ static float cwscale = 1.0; -static float chscale = 1.1; +static float chscale = 1.0; /* * word delimiter string @@ -60,6 +90,14 @@ int allowwindowops = 0; static double minlatency = 8; static double maxlatency = 33; +#if SYNC_PATCH +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; +#endif // SYNC_PATCH + /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. @@ -71,17 +109,19 @@ static unsigned int blinktimeout = 800; */ static unsigned int cursorthickness = 2; +#if BOXDRAW_PATCH /* * 1: render most of the lines/blocks characters without using the font for * perfect alignment between cells (U2500 - U259F except dashes/diagonals). * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. * 0: disable (render all U25XX glyphs normally from the font). */ -const int boxdraw = 1; -const int boxdraw_bold = 1; +const int boxdraw = 0; +const int boxdraw_bold = 0; /* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ const int boxdraw_braille = 0; +#endif // BOXDRAW_PATCH /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling @@ -90,7 +130,7 @@ const int boxdraw_braille = 0; static int bellvolume = 0; /* default TERM value */ -char *termname = "st-256color"; +char *termname = "xterm"; /* * spaces per tab @@ -107,41 +147,102 @@ char *termname = "st-256color"; * * stty tabs */ -unsigned int tabspaces = 2; +unsigned int tabspaces = 8; +#if ALPHA_PATCH /* bg opacity */ -float alpha = 1.0; +float alpha = 0.8; +#if ALPHA_GRADIENT_PATCH +float grad_alpha = 0.54; //alpha value that'll change +float stat_alpha = 0.46; //constant alpha value that'll get added to grad_alpha +#endif // ALPHA_GRADIENT_PATCH +#if ALPHA_FOCUS_HIGHLIGHT_PATCH +float alphaUnfocused = 0.6; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +#endif // ALPHA_PATCH /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - "#1b1b1b", /* black */ - "#c14d53", /* red */ - "#56966e", /* green */ - "#dcae61", /* yellow */ - "#6e95bd", /* blue */ - "#a56db1", /* magenta */ - "#6a9f98", /* cyan */ - "#b7b7b7", /* white */ + /* 8 normal colors */ + "#121111", /* black */ + "#c14d53", /* red */ + "#8fa176", /* green */ + "#dcae61", /* yellow */ + "#6e95bd", /* blue */ + "#a56db1", /* magenta */ + "#6a9f98", /* cyan */ + "#b7b7b7", /* white */ + + /* 8 bright colors */ "#242424", /* black */ - "#da4b52", /* red */ - "#57a274", /* green */ - "#fcc467", /* yellow */ - "#6fadea", /* blue */ - "#d466e9", /* magenta */ - "#63c4b5", /* cyan */ - "#d4d5d5", /* white */ + "#da4b52", /* red */ + "#8fa176", /* green */ + "#fcc467", /* yellow */ + "#6fadea", /* blue */ + "#d466e9", /* magenta */ + "#63c4b5", /* cyan */ + "#d4d5d5", /* white */ - [255] = 0, "#dbdfdf", /* default foreground colour */ - "#0c0c0c", /* default background colour */ - "#28292f", + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#add8e6", /* 256 -> cursor */ + "#555555", /* 257 -> rev cursor*/ + "#000000", /* 258 -> bg */ + "#e5e5e5", /* 259 -> fg */ }; -unsigned int defaultfg = 256; -unsigned int defaultbg = 257; -unsigned int defaultcs = 256; -static unsigned int defaultrcs = 257; +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +#if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH +unsigned int defaultbg = 0; +unsigned int bg = 17, bgUnfocused = 16; +#else +unsigned int defaultbg = 258; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +unsigned int defaultfg = 259; +unsigned int defaultcs = 256; +unsigned int defaultrcs = 257; + +#if VIM_BROWSE_PATCH +unsigned int const currentBg = 6, buffSize = 2048; +/// Enable double / triple click yanking / selection of word / line. +int const mouseYank = 1, mouseSelect = 0; +/// [Vim Browse] Colors for search results currently on screen. +unsigned int const highlightBg = 160, highlightFg = 15; +char const wDelS[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", wDelL[] = " \t"; +char *nmKeys [] = { ///< Shortcusts executed in normal mode + "R/Building\nN", "r/Building\n", "X/juli@machine\nN", "x/juli@machine\n", + "Q?[Leaving vim, starting execution]\n","F/: error:\nN", "f/: error:\n", "DQf" +}; +unsigned int const amountNmKeys = sizeof(nmKeys) / sizeof(*nmKeys); +/// Style of the {command, search} string shown in the right corner (y,v,V,/) +Glyph styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16}; +Glyph style[] = {{' ',ATTR_ITALIC|ATTR_FAINT,15,16}, {' ',ATTR_ITALIC,232,11}, + {' ', ATTR_ITALIC, 232, 4}, {' ', ATTR_ITALIC, 232, 12}}; +#endif // VIM_BROWSE_PATCH + +#if BLINKING_CURSOR_PATCH +/* + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 + * Default style of cursor + * 0: Blinking block + * 1: Blinking block (default) + * 2: Steady block ("â–ˆ") + * 3: Blinking underline + * 4: Steady underline ("_") + * 5: Blinking bar + * 6: Steady bar ("|") + * 7: Blinking st cursor + * 8: Steady st cursor + */ +static unsigned int cursorstyle = 1; +static Rune stcursor = 0x2603; /* snowman (U+2603) */ +#else /* * Default shape of cursor * 2: Block ("█") @@ -150,6 +251,7 @@ static unsigned int defaultrcs = 257; * 7: Snowman ("☃") */ static unsigned int cursorshape = 2; +#endif // BLINKING_CURSOR_PATCH /* * Default columns and rows numbers @@ -158,12 +260,19 @@ static unsigned int cursorshape = 2; static unsigned int cols = 80; static unsigned int rows = 24; +#if THEMED_CURSOR_PATCH +/* + * Default shape of the mouse cursor + */ +static char* mouseshape = "xterm"; +#else /* * Default colour and shape of the mouse cursor */ static unsigned int mouseshape = XC_xterm; static unsigned int mousefg = 7; static unsigned int mousebg = 0; +#endif // THEMED_CURSOR_PATCH /* * Color used to display font attributes when fontconfig selected a font which @@ -171,6 +280,54 @@ static unsigned int mousebg = 0; */ static unsigned int defaultattr = 11; +#if XRESOURCES_PATCH +/* + * Xresources preferences to load at startup + */ +ResourcePref resources[] = { + { "font", STRING, &font }, + { "color0", STRING, &colorname[0] }, + { "color1", STRING, &colorname[1] }, + { "color2", STRING, &colorname[2] }, + { "color3", STRING, &colorname[3] }, + { "color4", STRING, &colorname[4] }, + { "color5", STRING, &colorname[5] }, + { "color6", STRING, &colorname[6] }, + { "color7", STRING, &colorname[7] }, + { "color8", STRING, &colorname[8] }, + { "color9", STRING, &colorname[9] }, + { "color10", STRING, &colorname[10] }, + { "color11", STRING, &colorname[11] }, + { "color12", STRING, &colorname[12] }, + { "color13", STRING, &colorname[13] }, + { "color14", STRING, &colorname[14] }, + { "color15", STRING, &colorname[15] }, + { "background", STRING, &colorname[258] }, + { "foreground", STRING, &colorname[259] }, + { "cursorColor", STRING, &colorname[256] }, + { "termname", STRING, &termname }, + { "shell", STRING, &shell }, + { "minlatency", INTEGER, &minlatency }, + { "maxlatency", INTEGER, &maxlatency }, + { "blinktimeout", INTEGER, &blinktimeout }, + { "bellvolume", INTEGER, &bellvolume }, + { "tabspaces", INTEGER, &tabspaces }, + #if RELATIVEBORDER_PATCH + { "borderperc", INTEGER, &borderperc }, + #else + { "borderpx", INTEGER, &borderpx }, + #endif // RELATIVEBORDER_PATCH + { "cwscale", FLOAT, &cwscale }, + { "chscale", FLOAT, &chscale }, + #if ALPHA_PATCH + { "alpha", FLOAT, &alpha }, + #endif // ALPHA_PATCH + #if ALPHA_FOCUS_HIGHLIGHT_PATCH + { "alphaUnfocused",FLOAT, &alphaUnfocused }, + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +}; +#endif // XRESOURCES_PATCH + /* * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). * Note that if you want to use ShiftMask with selmasks, set this to an other @@ -183,36 +340,111 @@ static uint forcemousemod = ShiftMask; * Beware that overloading Button1 will disable the selection. */ static MouseShortcut mshortcuts[] = { - /* mask button function argument release */ - {ShiftMask, Button4, kscrollup, {.i = 1}}, - {ShiftMask, Button5, kscrolldown, {.i = 1}}, - {XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1}, - {ShiftMask, Button4, ttysend, {.s = "\033[5;2~"}}, - {XK_ANY_MOD, Button4, ttysend, {.s = "\031"}}, - {ShiftMask, Button5, ttysend, {.s = "\033[6;2~"}}, - {XK_ANY_MOD, Button5, ttysend, {.s = "\005"}}, + /* mask button function argument release screen */ + #if CLIPBOARD_PATCH + { XK_ANY_MOD, Button2, clippaste, {.i = 0}, 1 }, + #else + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + #endif // CLIPBOARD_PATCH + #if SCROLLBACK_MOUSE_PATCH + { ShiftMask, Button4, kscrollup, {.i = 1}, 0, S_PRI}, + { ShiftMask, Button5, kscrolldown, {.i = 1}, 0, S_PRI}, + #elif UNIVERSCROLL_PATCH + { XK_ANY_MOD, Button4, ttysend, {.s = "\033[5;2~"}, 0, S_PRI }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\033[6;2~"}, 0, S_PRI }, + #else + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + #endif // SCROLLBACK_MOUSE_PATCH + #if SCROLLBACK_MOUSE_ALTSCREEN_PATCH + { XK_NO_MOD, Button4, kscrollup, {.i = 1}, 0, S_PRI }, + { XK_NO_MOD, Button5, kscrolldown, {.i = 1}, 0, S_PRI }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"}, 0, S_ALT }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"}, 0, S_ALT }, + #else + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, + #endif // SCROLLBACK_MOUSE_ALTSCREEN_PATCH }; /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask -#define TERMMOD (ControlMask | ShiftMask) +#define TERMMOD (ControlMask|ShiftMask) + +#if EXTERNALPIPE_PATCH // example command +static char *openurlcmd[] = { "/bin/sh", "-c", + "xurls | dmenu -l 10 -w $WINDOWID | xargs -r open", + "externalpipe", NULL }; + +#if EXTERNALPIPEIN_PATCH // example command +static char *setbgcolorcmd[] = { "/bin/sh", "-c", + "printf '\033]11;#008000\007'", + "externalpipein", NULL }; +#endif // EXTERNALPIPEIN_PATCH +#endif // EXTERNALPIPE_PATCH static Shortcut shortcuts[] = { - /* mask keysym function argument */ - {XK_ANY_MOD, XK_Break, sendbreak, {.i = 0}}, - {ControlMask, XK_Print, toggleprinter, {.i = 0}}, - {ShiftMask, XK_Print, printscreen, {.i = 0}}, - {XK_ANY_MOD, XK_Print, printsel, {.i = 0}}, - {TERMMOD, XK_Prior, zoom, {.f = +1}}, - {TERMMOD, XK_Next, zoom, {.f = -1}}, - {TERMMOD, XK_Home, zoomreset, {.f = 0}}, - {TERMMOD, XK_C, clipcopy, {.i = 0}}, - {TERMMOD, XK_V, clippaste, {.i = 0}}, - {TERMMOD, XK_Y, selpaste, {.i = 0}}, - {ShiftMask, XK_Insert, selpaste, {.i = 0}}, - {TERMMOD, XK_Num_Lock, numlock, {.i = 0}}, - {ShiftMask, XK_Page_Up, kscrollup, {.i = -1}}, - {ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}}, + /* mask keysym function argument screen */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + #if ALPHA_PATCH + { TERMMOD, XK_O, changealpha, {.f = +0.05} }, + { TERMMOD, XK_P, changealpha, {.f = -0.05} }, + #if ALPHA_FOCUS_HIGHLIGHT_PATCH + //{ TERMMOD, XK_, changealphaunfocused, {.f = +0.05} }, + //{ TERMMOD, XK_, changealphaunfocused, {.f = -0.05} }, + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + #endif // ALPHA_PATCH + #if FULLSCREEN_PATCH + { XK_NO_MOD, XK_F11, fullscreen, {.i = 0} }, + { MODKEY, XK_Return, fullscreen, {.i = 0} }, + #endif // FULLSCREEN_PATCH + #if SCROLLBACK_PATCH + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1}, S_PRI }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}, S_PRI }, + #endif // SCROLLBACK_PATCH + #if CLIPBOARD_PATCH + { TERMMOD, XK_Y, clippaste, {.i = 0} }, + { ShiftMask, XK_Insert, clippaste, {.i = 0} }, + #else + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + #endif // CLIPBOARD_PATCH + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + #if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH + { MODKEY, XK_l, copyurl, {.i = 0} }, + #endif // COPYURL_PATCH + #if OPENCOPIED_PATCH + { MODKEY, XK_o, opencopied, {.v = "xdg-open"} }, + #endif // OPENCOPIED_PATCH + #if NEWTERM_PATCH + { TERMMOD, XK_Return, newterm, {.i = 0} }, + #endif // NEWTERM_PATCH + #if EXTERNALPIPE_PATCH + { TERMMOD, XK_U, externalpipe, { .v = openurlcmd } }, + #if EXTERNALPIPEIN_PATCH + { TERMMOD, XK_M, externalpipein, { .v = setbgcolorcmd } }, + #endif // EXTERNALPIPEIN_PATCH + #endif // EXTERNALPIPE_PATCH + #if KEYBOARDSELECT_PATCH + { TERMMOD, XK_Escape, keyboard_select, { 0 } }, + #endif // KEYBOARDSELECT_PATCH + #if ISO14755_PATCH + { TERMMOD, XK_I, iso14755, {.i = 0} }, + #endif // ISO14755_PATCH + #if INVERT_PATCH + { TERMMOD, XK_X, invert, { 0 } }, + #endif // INVERT_PATCH + #if VIM_BROWSE_PATCH + { MODKEY, XK_c, normalMode, {.i = 0} }, + #endif // VIM_BROWSE_PATCH }; /* @@ -236,234 +468,246 @@ static Shortcut shortcuts[] = { * position for a key. */ +#if !FIXKEYBOARDINPUT_PATCH /* * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) * to be mapped below, add them to this array. */ -static KeySym mappedkeys[] = {-1}; +static KeySym mappedkeys[] = { -1 }; +#endif // FIXKEYBOARDINPUT_PATCH /* * State bits to ignore when matching key or button events. By default, * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. */ -static uint ignoremod = Mod2Mask | XK_SWITCH_MOD; +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; +#if !FIXKEYBOARDINPUT_PATCH /* * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. */ static Key key[] = { - /* keysym mask string appkey appcursor */ - {XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, - {XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, - {XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, - {XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - {XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, - {XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, - {XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, - {XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, - {XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, - {XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, - {XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, - {XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, - {XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, - {XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, - {XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, - {XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, - {XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, - {XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - {XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, - {XK_KP_End, ControlMask, "\033[J", -1, 0}, - {XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, - {XK_KP_End, ShiftMask, "\033[K", -1, 0}, - {XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, - {XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, - {XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, - {XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - {XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, - {XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, - {XK_KP_Insert, ControlMask, "\033[L", -1, 0}, - {XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, - {XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - {XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - {XK_KP_Delete, ControlMask, "\033[M", -1, 0}, - {XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, - {XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, - {XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, - {XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - {XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, - {XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, - {XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, - {XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, - {XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, - {XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, - {XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, - {XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, - {XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, - {XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, - {XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, - {XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, - {XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, - {XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, - {XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, - {XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, - {XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, - {XK_Up, ShiftMask, "\033[1;2A", 0, 0}, - {XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, - {XK_Up, ShiftMask | Mod1Mask, "\033[1;4A", 0, 0}, - {XK_Up, ControlMask, "\033[1;5A", 0, 0}, - {XK_Up, ShiftMask | ControlMask, "\033[1;6A", 0, 0}, - {XK_Up, ControlMask | Mod1Mask, "\033[1;7A", 0, 0}, - {XK_Up, ShiftMask | ControlMask | Mod1Mask, "\033[1;8A", 0, 0}, - {XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, - {XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, - {XK_Down, ShiftMask, "\033[1;2B", 0, 0}, - {XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, - {XK_Down, ShiftMask | Mod1Mask, "\033[1;4B", 0, 0}, - {XK_Down, ControlMask, "\033[1;5B", 0, 0}, - {XK_Down, ShiftMask | ControlMask, "\033[1;6B", 0, 0}, - {XK_Down, ControlMask | Mod1Mask, "\033[1;7B", 0, 0}, - {XK_Down, ShiftMask | ControlMask | Mod1Mask, "\033[1;8B", 0, 0}, - {XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, - {XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, - {XK_Left, ShiftMask, "\033[1;2D", 0, 0}, - {XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, - {XK_Left, ShiftMask | Mod1Mask, "\033[1;4D", 0, 0}, - {XK_Left, ControlMask, "\033[1;5D", 0, 0}, - {XK_Left, ShiftMask | ControlMask, "\033[1;6D", 0, 0}, - {XK_Left, ControlMask | Mod1Mask, "\033[1;7D", 0, 0}, - {XK_Left, ShiftMask | ControlMask | Mod1Mask, "\033[1;8D", 0, 0}, - {XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, - {XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, - {XK_Right, ShiftMask, "\033[1;2C", 0, 0}, - {XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, - {XK_Right, ShiftMask | Mod1Mask, "\033[1;4C", 0, 0}, - {XK_Right, ControlMask, "\033[1;5C", 0, 0}, - {XK_Right, ShiftMask | ControlMask, "\033[1;6C", 0, 0}, - {XK_Right, ControlMask | Mod1Mask, "\033[1;7C", 0, 0}, - {XK_Right, ShiftMask | ControlMask | Mod1Mask, "\033[1;8C", 0, 0}, - {XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, - {XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, - {XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, - {XK_Return, Mod1Mask, "\033\r", 0, 0}, - {XK_Return, XK_ANY_MOD, "\r", 0, 0}, - {XK_Insert, ShiftMask, "\033[4l", -1, 0}, - {XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, - {XK_Insert, ControlMask, "\033[L", -1, 0}, - {XK_Insert, ControlMask, "\033[2;5~", +1, 0}, - {XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - {XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - {XK_Delete, ControlMask, "\033[M", -1, 0}, - {XK_Delete, ControlMask, "\033[3;5~", +1, 0}, - {XK_Delete, ShiftMask, "\033[2K", -1, 0}, - {XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, - {XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - {XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, - {XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, - {XK_Home, ShiftMask, "\033[2J", 0, -1}, - {XK_Home, ShiftMask, "\033[1;2H", 0, +1}, - {XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, - {XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - {XK_End, ControlMask, "\033[J", -1, 0}, - {XK_End, ControlMask, "\033[1;5F", +1, 0}, - {XK_End, ShiftMask, "\033[K", -1, 0}, - {XK_End, ShiftMask, "\033[1;2F", +1, 0}, - {XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, - {XK_Prior, ControlMask, "\033[5;5~", 0, 0}, - {XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, - {XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - {XK_Next, ControlMask, "\033[6;5~", 0, 0}, - {XK_Next, ShiftMask, "\033[6;2~", 0, 0}, - {XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - {XK_F1, XK_NO_MOD, "\033OP", 0, 0}, - {XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, - {XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, - {XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, - {XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, - {XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, - {XK_F2, XK_NO_MOD, "\033OQ", 0, 0}, - {XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, - {XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, - {XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, - {XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, - {XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, - {XK_F3, XK_NO_MOD, "\033OR", 0, 0}, - {XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, - {XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, - {XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, - {XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, - {XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, - {XK_F4, XK_NO_MOD, "\033OS", 0, 0}, - {XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, - {XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, - {XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, - {XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, - {XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, - {XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, - {XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, - {XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, - {XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, - {XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, - {XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, - {XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, - {XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, - {XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, - {XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, - {XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, - {XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, - {XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, - {XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, - {XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, - {XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, - {XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, - {XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, - {XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, - {XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, - {XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, - {XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, - {XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, - {XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, - {XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, - {XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, - {XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, - {XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, - {XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, - {XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, - {XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, - {XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, - {XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, - {XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, - {XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, - {XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, - {XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, - {XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, - {XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, - {XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, - {XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, - {XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, - {XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, - {XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, - {XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, - {XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, - {XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, - {XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, - {XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, - {XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, - {XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, - {XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, - {XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, - {XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, - {XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, - {XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, - {XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, - {XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, - {XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, - {XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, - {XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, - {XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + #if DELKEY_PATCH + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + #else + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + #endif // DELKEY_PATCH + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + #if DELKEY_PATCH + { XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + #else + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + #endif // DELKEY_PATCH + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, }; +#endif // FIXKEYBOARDINPUT_PATCH /* * Selection types' masks. @@ -473,13 +717,48 @@ static Key key[] = { * If no match is found, regular selection is used. */ static uint selmasks[] = { - [SEL_RECTANGULAR] = Mod1Mask, + [SEL_RECTANGULAR] = Mod1Mask, }; /* * Printable characters in ASCII, used to estimate the advance width * of single wide characters. */ -static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" - "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - "`abcdefghijklmnopqrstuvwxyz{|}~"; +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; + +#if RIGHTCLICKTOPLUMB_PATCH +/* + * plumb_cmd is run on mouse button 3 click, with argument set to + * current selection and with cwd set to the cwd of the active shell + */ +static char *plumb_cmd = "plumb"; +#endif // RIGHTCLICKTOPLUMB_PATCH + +#if UNDERCURL_PATCH +/** + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. + * + * Curly: Dunno how to draw it *shrug* + * _ _ _ _ + * ( ) ( ) ( ) ( ) + * (_) (_) (_) (_) + * + * Spiky: + * /\ /\ /\ /\ + * \/ \/ \/ + * + * Capped: + * _ _ _ + * / \ / \ / \ + * \_/ \_/ + */ +// Available styles +#define UNDERCURL_CURLY 0 +#define UNDERCURL_SPIKY 1 +#define UNDERCURL_CAPPED 2 +// Active style +#define UNDERCURL_STYLE UNDERCURL_SPIKY +#endif // UNDERCURL_PATCH diff --git a/config.h b/config.h index 12c328f..09fc32b 100644 --- a/config.h +++ b/config.h @@ -5,12 +5,38 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char *font = - "Iosevka Nerd Font:pixelsize=20:antialias=true:autohint=true"; +static char *font = "Iosevka Nerd Font:pixelsize=20:antialias=true:autohint=true"; +#if FONT2_PATCH +/* Spare fonts */ static char *font2[] = { "Noto Color Emoji:pixelsize=20:antialias=true:autohint=true", - "Material Design Icons Desktop:pixelsize=19"}; -static int borderpx = 0; + "Material Design Icons Desktop:pixelsize=19", +}; +#endif // FONT2_PATCH + +#if BACKGROUND_IMAGE_PATCH +/* + * background image + * expects farbfeld format + * pseudo transparency fixes coordinates to the screen origin + */ +static const char *bgfile = "/path/to/image.ff"; +static const int pseudotransparency = 0; +#endif // BACKGROUND_IMAGE_PATCH + +#if RELATIVEBORDER_PATCH +/* borderperc: percentage of cell width to use as a border + * 0 = no border, 100 = border width is same as cell width */ +int borderperc = 20; +#else +static int borderpx = 2; +#endif // RELATIVEBORDER_PATCH + +#if OPENURLONCLICK_PATCH +/* modkey options: ControlMask, ShiftMask or XK_ANY_MOD */ +static uint url_opener_modkey = XK_ANY_MOD; +static char *url_opener = "xdg-open"; +#endif // OPENURLONCLICK_PATCH /* * What program is execed by st depends of these precedence rules: @@ -27,11 +53,15 @@ char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ +#if SIXEL_PATCH +char *vtiden = "\033[?12;4c"; +#else char *vtiden = "\033[?6c"; +#endif /* Kerning / character bounding-box multipliers */ static float cwscale = 1.0; -static float chscale = 1.1; +static float chscale = 1.0; /* * word delimiter string @@ -60,6 +90,14 @@ int allowwindowops = 0; static double minlatency = 8; static double maxlatency = 33; +#if SYNC_PATCH +/* + * Synchronized-Update timeout in ms + * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec + */ +static uint su_timeout = 200; +#endif // SYNC_PATCH + /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. @@ -71,17 +109,19 @@ static unsigned int blinktimeout = 800; */ static unsigned int cursorthickness = 2; +#if BOXDRAW_PATCH /* * 1: render most of the lines/blocks characters without using the font for * perfect alignment between cells (U2500 - U259F except dashes/diagonals). * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. * 0: disable (render all U25XX glyphs normally from the font). */ -const int boxdraw = 1; -const int boxdraw_bold = 1; +const int boxdraw = 0; +const int boxdraw_bold = 0; /* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ const int boxdraw_braille = 0; +#endif // BOXDRAW_PATCH /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling @@ -90,7 +130,7 @@ const int boxdraw_braille = 0; static int bellvolume = 0; /* default TERM value */ -char *termname = "st-256color"; +char *termname = "xterm"; /* * spaces per tab @@ -107,41 +147,100 @@ char *termname = "st-256color"; * * stty tabs */ -unsigned int tabspaces = 2; +unsigned int tabspaces = 8; +#if ALPHA_PATCH /* bg opacity */ -float alpha = 1.0; +float alpha = 0.93; +#if ALPHA_GRADIENT_PATCH +float grad_alpha = 0.54; //alpha value that'll change +float stat_alpha = 0.46; //constant alpha value that'll get added to grad_alpha +#endif // ALPHA_GRADIENT_PATCH +#if ALPHA_FOCUS_HIGHLIGHT_PATCH +float alphaUnfocused = 0.6; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +#endif // ALPHA_PATCH /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - "#121111", /* black */ - "#c14d53", /* red */ - "#8fa176", /* green */ - "#dcae61", /* yellow */ - "#6e95bd", /* blue */ - "#a56db1", /* magenta */ - "#6a9f98", /* cyan */ - "#b7b7b7", /* white */ + /* 8 normal colors */ + "#121111", /* black */ + "#c14d53", /* red */ + "#8fa176", /* green */ + "#dcae61", /* yellow */ + "#6e95bd", /* blue */ + "#a56db1", /* magenta */ + "#6a9f98", /* cyan */ + "#b7b7b7", /* white */ + /* 8 bright colors */ + "#242424", /* black */ + "#da4b52", /* red */ + "#8fa176", /* green */ + "#fcc467", /* yellow */ + "#6fadea", /* blue */ + "#d466e9", /* magenta */ + "#63c4b5", /* cyan */ + "#d4d5d5", /* white */ - "#242424", /* black */ - "#da4b52", /* red */ - "#8fa176", /* green */ - "#fcc467", /* yellow */ - "#6fadea", /* blue */ - "#d466e9", /* magenta */ - "#63c4b5", /* cyan */ - "#d4d5d5", /* white */ + [255] = 0, - [255] = 0, "#dbdfdf", /* default foreground colour */ - "#121111", /* default background colour */ - "#28292f", + /* more colors can be added after 255 to use with DefaultXX */ + "#d4d5d5", /* 256 -> cursor */ + "#000000", /* 257 -> rev cursor*/ + "#010101", /* 258 -> bg */ + "#d4d5d5", /* 259 -> fg */ }; -unsigned int defaultfg = 256; -unsigned int defaultbg = 257; -unsigned int defaultcs = 256; -static unsigned int defaultrcs = 257; +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +#if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH +unsigned int defaultbg = 0; +unsigned int bg = 17, bgUnfocused = 16; +#else +unsigned int defaultbg = 258; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +unsigned int defaultfg = 259; +unsigned int defaultcs = 256; +unsigned int defaultrcs = 257; + +#if VIM_BROWSE_PATCH +unsigned int const currentBg = 6, buffSize = 2048; +/// Enable double / triple click yanking / selection of word / line. +int const mouseYank = 1, mouseSelect = 0; +/// [Vim Browse] Colors for search results currently on screen. +unsigned int const highlightBg = 160, highlightFg = 15; +char const wDelS[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", wDelL[] = " \t"; +char *nmKeys [] = { ///< Shortcusts executed in normal mode + "R/Building\nN", "r/Building\n", "X/juli@machine\nN", "x/juli@machine\n", + "Q?[Leaving vim, starting execution]\n","F/: error:\nN", "f/: error:\n", "DQf" +}; +unsigned int const amountNmKeys = sizeof(nmKeys) / sizeof(*nmKeys); +/// Style of the {command, search} string shown in the right corner (y,v,V,/) +Glyph styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16}; +Glyph style[] = {{' ',ATTR_ITALIC|ATTR_FAINT,15,16}, {' ',ATTR_ITALIC,232,11}, + {' ', ATTR_ITALIC, 232, 4}, {' ', ATTR_ITALIC, 232, 12}}; +#endif // VIM_BROWSE_PATCH + +#if BLINKING_CURSOR_PATCH +/* + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 + * Default style of cursor + * 0: Blinking block + * 1: Blinking block (default) + * 2: Steady block ("â–ˆ") + * 3: Blinking underline + * 4: Steady underline ("_") + * 5: Blinking bar + * 6: Steady bar ("|") + * 7: Blinking st cursor + * 8: Steady st cursor + */ +static unsigned int cursorstyle = 1; +static Rune stcursor = 0x2603; /* snowman (U+2603) */ +#else /* * Default shape of cursor * 2: Block ("█") @@ -150,6 +249,7 @@ static unsigned int defaultrcs = 257; * 7: Snowman ("☃") */ static unsigned int cursorshape = 2; +#endif // BLINKING_CURSOR_PATCH /* * Default columns and rows numbers @@ -158,12 +258,19 @@ static unsigned int cursorshape = 2; static unsigned int cols = 80; static unsigned int rows = 24; +#if THEMED_CURSOR_PATCH +/* + * Default shape of the mouse cursor + */ +static char* mouseshape = "xterm"; +#else /* * Default colour and shape of the mouse cursor */ static unsigned int mouseshape = XC_xterm; static unsigned int mousefg = 7; static unsigned int mousebg = 0; +#endif // THEMED_CURSOR_PATCH /* * Color used to display font attributes when fontconfig selected a font which @@ -171,6 +278,54 @@ static unsigned int mousebg = 0; */ static unsigned int defaultattr = 11; +#if XRESOURCES_PATCH +/* + * Xresources preferences to load at startup + */ +ResourcePref resources[] = { + { "font", STRING, &font }, + { "color0", STRING, &colorname[0] }, + { "color1", STRING, &colorname[1] }, + { "color2", STRING, &colorname[2] }, + { "color3", STRING, &colorname[3] }, + { "color4", STRING, &colorname[4] }, + { "color5", STRING, &colorname[5] }, + { "color6", STRING, &colorname[6] }, + { "color7", STRING, &colorname[7] }, + { "color8", STRING, &colorname[8] }, + { "color9", STRING, &colorname[9] }, + { "color10", STRING, &colorname[10] }, + { "color11", STRING, &colorname[11] }, + { "color12", STRING, &colorname[12] }, + { "color13", STRING, &colorname[13] }, + { "color14", STRING, &colorname[14] }, + { "color15", STRING, &colorname[15] }, + { "background", STRING, &colorname[258] }, + { "foreground", STRING, &colorname[259] }, + { "cursorColor", STRING, &colorname[256] }, + { "termname", STRING, &termname }, + { "shell", STRING, &shell }, + { "minlatency", INTEGER, &minlatency }, + { "maxlatency", INTEGER, &maxlatency }, + { "blinktimeout", INTEGER, &blinktimeout }, + { "bellvolume", INTEGER, &bellvolume }, + { "tabspaces", INTEGER, &tabspaces }, + #if RELATIVEBORDER_PATCH + { "borderperc", INTEGER, &borderperc }, + #else + { "borderpx", INTEGER, &borderpx }, + #endif // RELATIVEBORDER_PATCH + { "cwscale", FLOAT, &cwscale }, + { "chscale", FLOAT, &chscale }, + #if ALPHA_PATCH + { "alpha", FLOAT, &alpha }, + #endif // ALPHA_PATCH + #if ALPHA_FOCUS_HIGHLIGHT_PATCH + { "alphaUnfocused",FLOAT, &alphaUnfocused }, + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +}; +#endif // XRESOURCES_PATCH + /* * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). * Note that if you want to use ShiftMask with selmasks, set this to an other @@ -183,36 +338,111 @@ static uint forcemousemod = ShiftMask; * Beware that overloading Button1 will disable the selection. */ static MouseShortcut mshortcuts[] = { - /* mask button function argument release */ - {ShiftMask, Button4, kscrollup, {.i = 1}}, - {ShiftMask, Button5, kscrolldown, {.i = 1}}, - {XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1}, - {ShiftMask, Button4, ttysend, {.s = "\033[5;2~"}}, - {XK_ANY_MOD, Button4, ttysend, {.s = "\031"}}, - {ShiftMask, Button5, ttysend, {.s = "\033[6;2~"}}, - {XK_ANY_MOD, Button5, ttysend, {.s = "\005"}}, + /* mask button function argument release screen */ + #if CLIPBOARD_PATCH + { XK_ANY_MOD, Button2, clippaste, {.i = 0}, 1 }, + #else + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + #endif // CLIPBOARD_PATCH + #if SCROLLBACK_MOUSE_PATCH + { ShiftMask, Button4, kscrollup, {.i = 1}, 0, S_PRI}, + { ShiftMask, Button5, kscrolldown, {.i = 1}, 0, S_PRI}, + #elif UNIVERSCROLL_PATCH + { XK_ANY_MOD, Button4, ttysend, {.s = "\033[5;2~"}, 0, S_PRI }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\033[6;2~"}, 0, S_PRI }, + #else + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + #endif // SCROLLBACK_MOUSE_PATCH + #if SCROLLBACK_MOUSE_ALTSCREEN_PATCH + { XK_NO_MOD, Button4, kscrollup, {.i = 1}, 0, S_PRI }, + { XK_NO_MOD, Button5, kscrolldown, {.i = 1}, 0, S_PRI }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"}, 0, S_ALT }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"}, 0, S_ALT }, + #else + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, + #endif // SCROLLBACK_MOUSE_ALTSCREEN_PATCH }; /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask -#define TERMMOD (ControlMask | ShiftMask) +#define TERMMOD (ControlMask|ShiftMask) + +#if EXTERNALPIPE_PATCH // example command +static char *openurlcmd[] = { "/bin/sh", "-c", + "xurls | dmenu -l 10 -w $WINDOWID | xargs -r open", + "externalpipe", NULL }; + +#if EXTERNALPIPEIN_PATCH // example command +static char *setbgcolorcmd[] = { "/bin/sh", "-c", + "printf '\033]11;#008000\007'", + "externalpipein", NULL }; +#endif // EXTERNALPIPEIN_PATCH +#endif // EXTERNALPIPE_PATCH static Shortcut shortcuts[] = { - /* mask keysym function argument */ - {XK_ANY_MOD, XK_Break, sendbreak, {.i = 0}}, - {ControlMask, XK_Print, toggleprinter, {.i = 0}}, - {ShiftMask, XK_Print, printscreen, {.i = 0}}, - {XK_ANY_MOD, XK_Print, printsel, {.i = 0}}, - {TERMMOD, XK_Prior, zoom, {.f = +1}}, - {TERMMOD, XK_Next, zoom, {.f = -1}}, - {TERMMOD, XK_Home, zoomreset, {.f = 0}}, - {TERMMOD, XK_C, clipcopy, {.i = 0}}, - {TERMMOD, XK_V, clippaste, {.i = 0}}, - {TERMMOD, XK_Y, selpaste, {.i = 0}}, - {ShiftMask, XK_Insert, selpaste, {.i = 0}}, - {TERMMOD, XK_Num_Lock, numlock, {.i = 0}}, - {ShiftMask, XK_Page_Up, kscrollup, {.i = -1}}, - {ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}}, + /* mask keysym function argument screen */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + #if ALPHA_PATCH + { TERMMOD, XK_O, changealpha, {.f = +0.05} }, + { TERMMOD, XK_P, changealpha, {.f = -0.05} }, + #if ALPHA_FOCUS_HIGHLIGHT_PATCH + //{ TERMMOD, XK_, changealphaunfocused, {.f = +0.05} }, + //{ TERMMOD, XK_, changealphaunfocused, {.f = -0.05} }, + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + #endif // ALPHA_PATCH + #if FULLSCREEN_PATCH + { XK_NO_MOD, XK_F11, fullscreen, {.i = 0} }, + { MODKEY, XK_Return, fullscreen, {.i = 0} }, + #endif // FULLSCREEN_PATCH + #if SCROLLBACK_PATCH + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1}, S_PRI }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}, S_PRI }, + #endif // SCROLLBACK_PATCH + #if CLIPBOARD_PATCH + { TERMMOD, XK_Y, clippaste, {.i = 0} }, + { ShiftMask, XK_Insert, clippaste, {.i = 0} }, + #else + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + #endif // CLIPBOARD_PATCH + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + #if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH + { MODKEY, XK_l, copyurl, {.i = 0} }, + #endif // COPYURL_PATCH + #if OPENCOPIED_PATCH + { MODKEY, XK_o, opencopied, {.v = "xdg-open"} }, + #endif // OPENCOPIED_PATCH + #if NEWTERM_PATCH + { TERMMOD, XK_Return, newterm, {.i = 0} }, + #endif // NEWTERM_PATCH + #if EXTERNALPIPE_PATCH + { TERMMOD, XK_U, externalpipe, { .v = openurlcmd } }, + #if EXTERNALPIPEIN_PATCH + { TERMMOD, XK_M, externalpipein, { .v = setbgcolorcmd } }, + #endif // EXTERNALPIPEIN_PATCH + #endif // EXTERNALPIPE_PATCH + #if KEYBOARDSELECT_PATCH + { TERMMOD, XK_Escape, keyboard_select, { 0 } }, + #endif // KEYBOARDSELECT_PATCH + #if ISO14755_PATCH + { TERMMOD, XK_I, iso14755, {.i = 0} }, + #endif // ISO14755_PATCH + #if INVERT_PATCH + { TERMMOD, XK_X, invert, { 0 } }, + #endif // INVERT_PATCH + #if VIM_BROWSE_PATCH + { MODKEY, XK_c, normalMode, {.i = 0} }, + #endif // VIM_BROWSE_PATCH }; /* @@ -236,234 +466,246 @@ static Shortcut shortcuts[] = { * position for a key. */ +#if !FIXKEYBOARDINPUT_PATCH /* * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) * to be mapped below, add them to this array. */ -static KeySym mappedkeys[] = {-1}; +static KeySym mappedkeys[] = { -1 }; +#endif // FIXKEYBOARDINPUT_PATCH /* * State bits to ignore when matching key or button events. By default, * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. */ -static uint ignoremod = Mod2Mask | XK_SWITCH_MOD; +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; +#if !FIXKEYBOARDINPUT_PATCH /* * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. */ static Key key[] = { - /* keysym mask string appkey appcursor */ - {XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, - {XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, - {XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, - {XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - {XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, - {XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, - {XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, - {XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, - {XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, - {XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, - {XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, - {XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, - {XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, - {XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, - {XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, - {XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, - {XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, - {XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - {XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, - {XK_KP_End, ControlMask, "\033[J", -1, 0}, - {XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, - {XK_KP_End, ShiftMask, "\033[K", -1, 0}, - {XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, - {XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, - {XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, - {XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - {XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, - {XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, - {XK_KP_Insert, ControlMask, "\033[L", -1, 0}, - {XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, - {XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - {XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - {XK_KP_Delete, ControlMask, "\033[M", -1, 0}, - {XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, - {XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, - {XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, - {XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - {XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, - {XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, - {XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, - {XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, - {XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, - {XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, - {XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, - {XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, - {XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, - {XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, - {XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, - {XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, - {XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, - {XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, - {XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, - {XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, - {XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, - {XK_Up, ShiftMask, "\033[1;2A", 0, 0}, - {XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, - {XK_Up, ShiftMask | Mod1Mask, "\033[1;4A", 0, 0}, - {XK_Up, ControlMask, "\033[1;5A", 0, 0}, - {XK_Up, ShiftMask | ControlMask, "\033[1;6A", 0, 0}, - {XK_Up, ControlMask | Mod1Mask, "\033[1;7A", 0, 0}, - {XK_Up, ShiftMask | ControlMask | Mod1Mask, "\033[1;8A", 0, 0}, - {XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, - {XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, - {XK_Down, ShiftMask, "\033[1;2B", 0, 0}, - {XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, - {XK_Down, ShiftMask | Mod1Mask, "\033[1;4B", 0, 0}, - {XK_Down, ControlMask, "\033[1;5B", 0, 0}, - {XK_Down, ShiftMask | ControlMask, "\033[1;6B", 0, 0}, - {XK_Down, ControlMask | Mod1Mask, "\033[1;7B", 0, 0}, - {XK_Down, ShiftMask | ControlMask | Mod1Mask, "\033[1;8B", 0, 0}, - {XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, - {XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, - {XK_Left, ShiftMask, "\033[1;2D", 0, 0}, - {XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, - {XK_Left, ShiftMask | Mod1Mask, "\033[1;4D", 0, 0}, - {XK_Left, ControlMask, "\033[1;5D", 0, 0}, - {XK_Left, ShiftMask | ControlMask, "\033[1;6D", 0, 0}, - {XK_Left, ControlMask | Mod1Mask, "\033[1;7D", 0, 0}, - {XK_Left, ShiftMask | ControlMask | Mod1Mask, "\033[1;8D", 0, 0}, - {XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, - {XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, - {XK_Right, ShiftMask, "\033[1;2C", 0, 0}, - {XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, - {XK_Right, ShiftMask | Mod1Mask, "\033[1;4C", 0, 0}, - {XK_Right, ControlMask, "\033[1;5C", 0, 0}, - {XK_Right, ShiftMask | ControlMask, "\033[1;6C", 0, 0}, - {XK_Right, ControlMask | Mod1Mask, "\033[1;7C", 0, 0}, - {XK_Right, ShiftMask | ControlMask | Mod1Mask, "\033[1;8C", 0, 0}, - {XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, - {XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, - {XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, - {XK_Return, Mod1Mask, "\033\r", 0, 0}, - {XK_Return, XK_ANY_MOD, "\r", 0, 0}, - {XK_Insert, ShiftMask, "\033[4l", -1, 0}, - {XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, - {XK_Insert, ControlMask, "\033[L", -1, 0}, - {XK_Insert, ControlMask, "\033[2;5~", +1, 0}, - {XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - {XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - {XK_Delete, ControlMask, "\033[M", -1, 0}, - {XK_Delete, ControlMask, "\033[3;5~", +1, 0}, - {XK_Delete, ShiftMask, "\033[2K", -1, 0}, - {XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, - {XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, - {XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - {XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, - {XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, - {XK_Home, ShiftMask, "\033[2J", 0, -1}, - {XK_Home, ShiftMask, "\033[1;2H", 0, +1}, - {XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, - {XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - {XK_End, ControlMask, "\033[J", -1, 0}, - {XK_End, ControlMask, "\033[1;5F", +1, 0}, - {XK_End, ShiftMask, "\033[K", -1, 0}, - {XK_End, ShiftMask, "\033[1;2F", +1, 0}, - {XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, - {XK_Prior, ControlMask, "\033[5;5~", 0, 0}, - {XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, - {XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - {XK_Next, ControlMask, "\033[6;5~", 0, 0}, - {XK_Next, ShiftMask, "\033[6;2~", 0, 0}, - {XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - {XK_F1, XK_NO_MOD, "\033OP", 0, 0}, - {XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, - {XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, - {XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, - {XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, - {XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, - {XK_F2, XK_NO_MOD, "\033OQ", 0, 0}, - {XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, - {XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, - {XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, - {XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, - {XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, - {XK_F3, XK_NO_MOD, "\033OR", 0, 0}, - {XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, - {XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, - {XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, - {XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, - {XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, - {XK_F4, XK_NO_MOD, "\033OS", 0, 0}, - {XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, - {XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, - {XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, - {XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, - {XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, - {XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, - {XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, - {XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, - {XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, - {XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, - {XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, - {XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, - {XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, - {XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, - {XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, - {XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, - {XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, - {XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, - {XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, - {XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, - {XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, - {XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, - {XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, - {XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, - {XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, - {XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, - {XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, - {XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, - {XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, - {XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, - {XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, - {XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, - {XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, - {XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, - {XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, - {XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, - {XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, - {XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, - {XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, - {XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, - {XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, - {XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, - {XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, - {XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, - {XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, - {XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, - {XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, - {XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, - {XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, - {XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, - {XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, - {XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, - {XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, - {XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, - {XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, - {XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, - {XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, - {XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, - {XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, - {XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, - {XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, - {XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, - {XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, - {XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, - {XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, - {XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, - {XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + #if DELKEY_PATCH + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + #else + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + #endif // DELKEY_PATCH + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + #if DELKEY_PATCH + { XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0}, + #else + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + #endif // DELKEY_PATCH + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, }; +#endif // FIXKEYBOARDINPUT_PATCH /* * Selection types' masks. @@ -473,13 +715,48 @@ static Key key[] = { * If no match is found, regular selection is used. */ static uint selmasks[] = { - [SEL_RECTANGULAR] = Mod1Mask, + [SEL_RECTANGULAR] = Mod1Mask, }; /* * Printable characters in ASCII, used to estimate the advance width * of single wide characters. */ -static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" - "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - "`abcdefghijklmnopqrstuvwxyz{|}~"; +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; + +#if RIGHTCLICKTOPLUMB_PATCH +/* + * plumb_cmd is run on mouse button 3 click, with argument set to + * current selection and with cwd set to the cwd of the active shell + */ +static char *plumb_cmd = "plumb"; +#endif // RIGHTCLICKTOPLUMB_PATCH + +#if UNDERCURL_PATCH +/** + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. + * + * Curly: Dunno how to draw it *shrug* + * _ _ _ _ + * ( ) ( ) ( ) ( ) + * (_) (_) (_) (_) + * + * Spiky: + * /\ /\ /\ /\ + * \/ \/ \/ + * + * Capped: + * _ _ _ + * / \ / \ / \ + * \_/ \_/ + */ +// Available styles +#define UNDERCURL_CURLY 0 +#define UNDERCURL_SPIKY 1 +#define UNDERCURL_CAPPED 2 +// Active style +#define UNDERCURL_STYLE UNDERCURL_SPIKY +#endif // UNDERCURL_PATCH diff --git a/config.mk b/config.mk index 624cc77..36e98ce 100644 --- a/config.mk +++ b/config.mk @@ -1,11 +1,10 @@ # st version -VERSION = 0.8.5 +VERSION = 0.9 # Customize below to fit your system # paths PREFIX = /usr/local -APPPREFIX = $(PREFIX)/share/applications MANPREFIX = $(PREFIX)/share/man X11INC = /usr/X11R6/include @@ -13,15 +12,30 @@ X11LIB = /usr/X11R6/lib PKG_CONFIG = pkg-config -# includes and libs -INCS = -I. -I/usr/include -I${X11INC} \ +# Uncomment this for the alpha patch / ALPHA_PATCH +#XRENDER = -lXrender + +# Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH +#XCURSOR = -lXcursor + +# Uncomment the lines below for the ligatures patch / LIGATURES_PATCH +#LIGATURES_C = hb.c +#LIGATURES_H = hb.h +#LIGATURES_INC = `$(PKG_CONFIG) --cflags harfbuzz` +#LIGATURES_LIBS = `$(PKG_CONFIG) --libs harfbuzz` + +# Uncomment this for the SIXEL patch / SIXEL_PATCH +#SIXEL_C = sixel.c sixel_hls.c + +# includes and libs, uncomment harfbuzz for the ligatures patch +INCS = -I$(X11INC) \ `$(PKG_CONFIG) --cflags fontconfig` \ `$(PKG_CONFIG) --cflags freetype2` \ - `$(PKG_CONFIG) --cflags harfbuzz` -LIBS = -L/usr/lib -lc -L${X11LIB} -lm -lrt -lX11 -lutil -lXft -lXrender\ + $(LIGATURES_INC) +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${XRENDER} ${XCURSOR}\ `$(PKG_CONFIG) --libs fontconfig` \ `$(PKG_CONFIG) --libs freetype2` \ - `$(PKG_CONFIG) --libs harfbuzz` + $(LIGATURES_LIBS) # flags STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 @@ -31,8 +45,8 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) # OpenBSD: #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ -# `$(PKG_CONFIG) --libs fontconfig` \ -# `$(PKG_CONFIG) --libs freetype2` +# `pkg-config --libs fontconfig` \ +# `pkg-config --libs freetype2` #MANPREFIX = ${PREFIX}/man # compiler and linker diff --git a/hb.c b/hb.c new file mode 100644 index 0000000..db845e7 --- /dev/null +++ b/hb.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "hb.h" + +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } + +hb_font_t *hbfindfont(XftFont *match); + +typedef struct { + XftFont *match; + hb_font_t *font; +} HbFontMatch; + +static int hbfontslen = 0; +static HbFontMatch *hbfontcache = NULL; + +/* + * Poplulate the array with a list of font features, wrapped in FEATURE macro, + * e. g. + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') + */ +hb_feature_t features[] = { 0 }; + +void +hbunloadfonts() +{ + for (int i = 0; i < hbfontslen; i++) { + hb_font_destroy(hbfontcache[i].font); + XftUnlockFace(hbfontcache[i].match); + } + + if (hbfontcache != NULL) { + free(hbfontcache); + hbfontcache = NULL; + } + hbfontslen = 0; +} + +hb_font_t * +hbfindfont(XftFont *match) +{ + for (int i = 0; i < hbfontslen; i++) { + if (hbfontcache[i].match == match) + return hbfontcache[i].font; + } + + /* Font not found in cache, caching it now. */ + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); + FT_Face face = XftLockFace(match); + hb_font_t *font = hb_ft_font_create(face, NULL); + if (font == NULL) + die("Failed to load Harfbuzz font."); + + hbfontcache[hbfontslen].match = match; + hbfontcache[hbfontslen].font = font; + hbfontslen += 1; + + return font; +} + +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { + Rune rune; + ushort mode = USHRT_MAX; + unsigned int glyph_count; + int i, end = start + length; + + hb_font_t *font = hbfindfont(xfont); + if (font == NULL) + return; + + hb_buffer_t *buffer = hb_buffer_create(); + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); + + /* Fill buffer with codepoints. */ + for (i = start; i < end; i++) { + rune = glyphs[i].u; + mode = glyphs[i].mode; + if (mode & ATTR_WDUMMY) + rune = 0x0020; + hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); + } + + /* Shape the segment. */ + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); + + /* Get new glyph info. */ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); + + /** Fill the output. */ + data->buffer = buffer; + data->glyphs = info; + data->positions = pos; + data->count = glyph_count; +} + +void hbcleanup(HbTransformData *data) { + hb_buffer_destroy(data->buffer); + memset(data, 0, sizeof(HbTransformData)); +} diff --git a/hb.h b/hb.h new file mode 100644 index 0000000..3b0ef44 --- /dev/null +++ b/hb.h @@ -0,0 +1,14 @@ +#include +#include +#include + +typedef struct { + hb_buffer_t *buffer; + hb_glyph_info_t *glyphs; + hb_glyph_position_t *positions; + unsigned int count; +} HbTransformData; + +void hbunloadfonts(); +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); +void hbcleanup(HbTransformData *); diff --git a/patch/alpha.c b/patch/alpha.c new file mode 100644 index 0000000..146bf4b --- /dev/null +++ b/patch/alpha.c @@ -0,0 +1,30 @@ +float +clamp(float value, float lower, float upper) { + if (value < lower) + return lower; + if (value > upper) + return upper; + return value; +} + +void +changealpha(const Arg *arg) +{ + if ((alpha > 0 && arg->f < 0) || (alpha < 1 && arg->f > 0)) + alpha += arg->f; + alpha = clamp(alpha, 0.0, 1.0); + xloadcols(); + redraw(); +} + +#if ALPHA_FOCUS_HIGHLIGHT_PATCH +void +changealphaunfocused(const Arg *arg) +{ + if ((alphaUnfocused > 0 && arg->f < 0) || (alphaUnfocused < 1 && arg->f > 0)) + alphaUnfocused += arg->f; + alphaUnfocused = clamp(alphaUnfocused, 0.0, 1.0); + xloadcols(); + redraw(); +} +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH diff --git a/patch/alpha.h b/patch/alpha.h new file mode 100644 index 0000000..2d01830 --- /dev/null +++ b/patch/alpha.h @@ -0,0 +1,5 @@ +static float clamp(float value, float lower, float upper); +static void changealpha(const Arg *); +#if ALPHA_FOCUS_HIGHLIGHT_PATCH +static void changealphaunfocused(const Arg *arg); +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH diff --git a/patch/background_image_x.c b/patch/background_image_x.c new file mode 100644 index 0000000..3bbb189 --- /dev/null +++ b/patch/background_image_x.c @@ -0,0 +1,106 @@ +void +updatexy() +{ + Window child; + XTranslateCoordinates(xw.dpy, xw.win, DefaultRootWindow(xw.dpy), 0, 0, &win.x, &win.y, &child); +} + +/* + * load farbfeld file to XImage + */ +XImage* +loadff(const char *filename) +{ + uint32_t i, hdr[4], w, h, size; + uint64_t *data; + FILE *f = fopen(filename, "rb"); + + if (f == NULL) { + fprintf(stderr, "could not load background image.\n"); + return NULL; + } + + if (fread(hdr, sizeof(*hdr), LEN(hdr), f) != LEN(hdr)) { + fprintf(stderr, "fread: %s\n", ferror(f) ? "" : "Unexpected end of file reading header"); + fclose(f); + return NULL; + } + + if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) { + fprintf(stderr, "Invalid magic value\n"); + fclose(f); + return NULL; + } + + w = ntohl(hdr[2]); + h = ntohl(hdr[3]); + size = w * h; + data = xmalloc(size * sizeof(uint64_t)); + + if (fread(data, sizeof(uint64_t), size, f) != size) { + fprintf(stderr, "fread: %s\n", ferror(f) ? "" : "Unexpected end of file reading data"); + fclose(f); + return NULL; + } + + fclose(f); + + for (i = 0; i < size; i++) + data[i] = (data[i] & 0x00000000000000FF) << 16 | + (data[i] & 0x0000000000FF0000) >> 8 | + (data[i] & 0x000000FF00000000) >> 32 | + (data[i] & 0x00FF000000000000) >> 24; + + #if ALPHA_PATCH + XImage *xi = XCreateImage(xw.dpy, xw.vis, xw.depth, ZPixmap, 0, + (char *)data, w, h, 32, w * 8); + #else + XImage *xi = XCreateImage(xw.dpy, DefaultVisual(xw.dpy, xw.scr), + DefaultDepth(xw.dpy, xw.scr), ZPixmap, 0, + (char *)data, w, h, 32, w * 8); + #endif // ALPHA_PATCH + xi->bits_per_pixel = 64; + return xi; +} + +/* + * initialize background image + */ +void +bginit() +{ + XGCValues gcvalues; + Drawable bgimg; + XImage *bgxi = loadff(bgfile); + + memset(&gcvalues, 0, sizeof(gcvalues)); + xw.bggc = XCreateGC(xw.dpy, xw.win, 0, &gcvalues); + if (!bgxi) + return; + #if ALPHA_PATCH + bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height, + xw.depth); + #else + bgimg = XCreatePixmap(xw.dpy, xw.win, bgxi->width, bgxi->height, + DefaultDepth(xw.dpy, xw.scr)); + #endif // ALPHA_PATCH + XPutImage(xw.dpy, bgimg, dc.gc, bgxi, 0, 0, 0, 0, bgxi->width, bgxi->height); + XDestroyImage(bgxi); + XSetTile(xw.dpy, xw.bggc, bgimg); + XSetFillStyle(xw.dpy, xw.bggc, FillTiled); + if (pseudotransparency) { + updatexy(); + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); + } +} + +#if BACKGROUND_IMAGE_RELOAD_PATCH +void +reload_image() +{ + XFreeGC(xw.dpy, xw.bggc); + bginit(); + redraw(); +} +#endif // XRESOURCES_RELOAD_PATCH diff --git a/patch/background_image_x.h b/patch/background_image_x.h new file mode 100644 index 0000000..e96edd2 --- /dev/null +++ b/patch/background_image_x.h @@ -0,0 +1,6 @@ +#include + +static void updatexy(void); +static XImage *loadff(const char *); +static void bginit(); +static void reload_image(); diff --git a/patch/boxdraw.c b/patch/boxdraw.c new file mode 100644 index 0000000..3cc5b51 --- /dev/null +++ b/patch/boxdraw.c @@ -0,0 +1,192 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +#include + +/* Rounded non-negative integers division of n / d */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ + xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ + Rune block = u & ~0xff; + return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || + (boxdraw_braille && block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ + if (boxdraw_braille && (g->u & ~0xff) == 0x2800) + return BRL | (uint8_t)g->u; + if (boxdraw_bold && (g->mode & ATTR_BOLD)) + return BDB | boxdata[(uint8_t)g->u]; + return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, + const XftGlyphFontSpec *specs, int len) +{ + for ( ; len-- > 0; x += cw, specs++) + drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ + ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ + if (bd & (BDL | BDA)) { + /* lines (light/double/heavy/arcs) */ + drawboxlines(x, y, w, h, fg, bd); + + } else if (cat == BBD) { + /* lower (8-X)/8 block */ + int d = DIV((uint8_t)bd * h, 8); + XftDrawRect(xd, fg, x, y + d, w, h - d); + + } else if (cat == BBU) { + /* upper X/8 block */ + XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + + } else if (cat == BBL) { + /* left X/8 block */ + XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + + } else if (cat == BBR) { + /* right (8-X)/8 block */ + int d = DIV((uint8_t)bd * w, 8); + XftDrawRect(xd, fg, x + d, y, w - d, h); + + } else if (cat == BBQ) { + /* Quadrants */ + int w2 = DIV(w, 2), h2 = DIV(h, 2); + if (bd & TL) + XftDrawRect(xd, fg, x, y, w2, h2); + if (bd & TR) + XftDrawRect(xd, fg, x + w2, y, w - w2, h2); + if (bd & BL) + XftDrawRect(xd, fg, x, y + h2, w2, h - h2); + if (bd & BR) + XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + + } else if (bd & BBS) { + /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ + int d = (uint8_t)bd; + XftColor xfc; + XRenderColor xrc = { .alpha = 0xffff }; + + xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); + xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); + xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + + XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); + XftDrawRect(xd, &xfc, x, y, w, h); + XftColorFree(xdpy, xvis, xcmap, &xfc); + + } else if (cat == BRL) { + /* braille, each data bit corresponds to one dot at 2x4 grid */ + int w1 = DIV(w, 2); + int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + + if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); + if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); + if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); + if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); + if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); + if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); + if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); + if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + + } +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ + /* s: stem thickness. width/8 roughly matches underscore thickness. */ + /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ + /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ + int mwh = MIN(w, h); + int base_s = MAX(1, DIV(mwh, 8)); + int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ + int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; + int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); + /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ + /* The base length (per direction till edge) includes this square. */ + + int light = bd & (LL | LU | LR | LD); + int double_ = bd & (DL | DU | DR | DD); + + if (light) { + /* d: additional (negative) length to not-draw the center */ + /* texel - at arcs and avoid drawing inside (some) doubles */ + int arc = bd & BDA; + int multi_light = light & (light - 1); + int multi_double = double_ & (double_ - 1); + /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ + int d = arc || (multi_double && !multi_light) ? -s : 0; + + if (bd & LL) + XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); + if (bd & LU) + XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); + if (bd & LR) + XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); + if (bd & LD) + XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); + } + + /* double lines - also align with light to form heavy when combined */ + if (double_) { + /* + * going clockwise, for each double-ray: p is additional length + * to the single-ray nearer to the previous direction, and n to + * the next. p and n adjust from the base length to lengths + * which consider other doubles - shorter to avoid intersections + * (p, n), or longer to draw the far-corner texel (n). + */ + int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; + if (dl) { + int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; + XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); + XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); + } + if (du) { + int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; + XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); + XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); + } + if (dr) { + int p = du ? -s : 0, n = dd ? -s : du ? s : 0; + XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); + XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); + } + if (dd) { + int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; + XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); + XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); + } + } +} \ No newline at end of file diff --git a/patch/boxdraw.h b/patch/boxdraw.h new file mode 100644 index 0000000..12df637 --- /dev/null +++ b/patch/boxdraw.h @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +/* + * U+25XX codepoints data + * + * References: + * http://www.unicode.org/charts/PDF/U2500.pdf + * http://www.unicode.org/charts/PDF/U2580.pdf + * + * Test page: + * https://github.com/GNOME/vte/blob/master/doc/boxes.txt + */ + +/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ +/* Categories (mutually exclusive except BDB): */ +/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ +#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ +#define BDA (1<<9) /* Box Draw Arc (light) */ + +#define BBD (1<<10) /* Box Block Down (lower) X/8 */ +#define BBL (2<<10) /* Box Block Left X/8 */ +#define BBU (3<<10) /* Box Block Upper X/8 */ +#define BBR (4<<10) /* Box Block Right X/8 */ +#define BBQ (5<<10) /* Box Block Quadrants */ +#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ + +#define BBS (1<<14) /* Box Block Shades */ +#define BDB (1<<15) /* Box Draw is Bold */ + +/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ +/* Heavy is light+double (literally drawing light+double align to form heavy) */ +#define LL (1<<0) +#define LU (1<<1) +#define LR (1<<2) +#define LD (1<<3) +#define LH (LL+LR) +#define LV (LU+LD) + +#define DL (1<<4) +#define DU (1<<5) +#define DR (1<<6) +#define DD (1<<7) +#define DH (DL+DR) +#define DV (DU+DD) + +#define HL (LL+DL) +#define HU (LU+DU) +#define HR (LR+DR) +#define HD (LD+DD) +#define HH (HL+HR) +#define HV (HU+HD) + +/* (BBQ) Quadrants Top/Bottom x Left/Right */ +#define TL (1<<0) +#define TR (1<<1) +#define BL (1<<2) +#define BR (1<<3) + +/* Data for U+2500 - U+259F except dashes/diagonals */ +static const unsigned short boxdata[256] = { + /* light lines */ + [0x00] = BDL + LH, /* light horizontal */ + [0x02] = BDL + LV, /* light vertical */ + [0x0c] = BDL + LD + LR, /* light down and right */ + [0x10] = BDL + LD + LL, /* light down and left */ + [0x14] = BDL + LU + LR, /* light up and right */ + [0x18] = BDL + LU + LL, /* light up and left */ + [0x1c] = BDL + LV + LR, /* light vertical and right */ + [0x24] = BDL + LV + LL, /* light vertical and left */ + [0x2c] = BDL + LH + LD, /* light horizontal and down */ + [0x34] = BDL + LH + LU, /* light horizontal and up */ + [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ + [0x74] = BDL + LL, /* light left */ + [0x75] = BDL + LU, /* light up */ + [0x76] = BDL + LR, /* light right */ + [0x77] = BDL + LD, /* light down */ + + /* heavy [+light] lines */ + [0x01] = BDL + HH, + [0x03] = BDL + HV, + [0x0d] = BDL + HR + LD, + [0x0e] = BDL + HD + LR, + [0x0f] = BDL + HD + HR, + [0x11] = BDL + HL + LD, + [0x12] = BDL + HD + LL, + [0x13] = BDL + HD + HL, + [0x15] = BDL + HR + LU, + [0x16] = BDL + HU + LR, + [0x17] = BDL + HU + HR, + [0x19] = BDL + HL + LU, + [0x1a] = BDL + HU + LL, + [0x1b] = BDL + HU + HL, + [0x1d] = BDL + HR + LV, + [0x1e] = BDL + HU + LD + LR, + [0x1f] = BDL + HD + LR + LU, + [0x20] = BDL + HV + LR, + [0x21] = BDL + HU + HR + LD, + [0x22] = BDL + HD + HR + LU, + [0x23] = BDL + HV + HR, + [0x25] = BDL + HL + LV, + [0x26] = BDL + HU + LD + LL, + [0x27] = BDL + HD + LU + LL, + [0x28] = BDL + HV + LL, + [0x29] = BDL + HU + HL + LD, + [0x2a] = BDL + HD + HL + LU, + [0x2b] = BDL + HV + HL, + [0x2d] = BDL + HL + LD + LR, + [0x2e] = BDL + HR + LL + LD, + [0x2f] = BDL + HH + LD, + [0x30] = BDL + HD + LH, + [0x31] = BDL + HD + HL + LR, + [0x32] = BDL + HR + HD + LL, + [0x33] = BDL + HH + HD, + [0x35] = BDL + HL + LU + LR, + [0x36] = BDL + HR + LU + LL, + [0x37] = BDL + HH + LU, + [0x38] = BDL + HU + LH, + [0x39] = BDL + HU + HL + LR, + [0x3a] = BDL + HU + HR + LL, + [0x3b] = BDL + HH + HU, + [0x3d] = BDL + HL + LV + LR, + [0x3e] = BDL + HR + LV + LL, + [0x3f] = BDL + HH + LV, + [0x40] = BDL + HU + LH + LD, + [0x41] = BDL + HD + LH + LU, + [0x42] = BDL + HV + LH, + [0x43] = BDL + HU + HL + LD + LR, + [0x44] = BDL + HU + HR + LD + LL, + [0x45] = BDL + HD + HL + LU + LR, + [0x46] = BDL + HD + HR + LU + LL, + [0x47] = BDL + HH + HU + LD, + [0x48] = BDL + HH + HD + LU, + [0x49] = BDL + HV + HL + LR, + [0x4a] = BDL + HV + HR + LL, + [0x4b] = BDL + HV + HH, + [0x78] = BDL + HL, + [0x79] = BDL + HU, + [0x7a] = BDL + HR, + [0x7b] = BDL + HD, + [0x7c] = BDL + HR + LL, + [0x7d] = BDL + HD + LU, + [0x7e] = BDL + HL + LR, + [0x7f] = BDL + HU + LD, + + /* double [+light] lines */ + [0x50] = BDL + DH, + [0x51] = BDL + DV, + [0x52] = BDL + DR + LD, + [0x53] = BDL + DD + LR, + [0x54] = BDL + DR + DD, + [0x55] = BDL + DL + LD, + [0x56] = BDL + DD + LL, + [0x57] = BDL + DL + DD, + [0x58] = BDL + DR + LU, + [0x59] = BDL + DU + LR, + [0x5a] = BDL + DU + DR, + [0x5b] = BDL + DL + LU, + [0x5c] = BDL + DU + LL, + [0x5d] = BDL + DL + DU, + [0x5e] = BDL + DR + LV, + [0x5f] = BDL + DV + LR, + [0x60] = BDL + DV + DR, + [0x61] = BDL + DL + LV, + [0x62] = BDL + DV + LL, + [0x63] = BDL + DV + DL, + [0x64] = BDL + DH + LD, + [0x65] = BDL + DD + LH, + [0x66] = BDL + DD + DH, + [0x67] = BDL + DH + LU, + [0x68] = BDL + DU + LH, + [0x69] = BDL + DH + DU, + [0x6a] = BDL + DH + LV, + [0x6b] = BDL + DV + LH, + [0x6c] = BDL + DH + DV, + + /* (light) arcs */ + [0x6d] = BDA + LD + LR, + [0x6e] = BDA + LD + LL, + [0x6f] = BDA + LU + LL, + [0x70] = BDA + LU + LR, + + /* Lower (Down) X/8 block (data is 8 - X) */ + [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, + [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, + + /* Left X/8 block (data is X) */ + [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, + [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, + + /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ + [0x80] = BBU + 4, [0x94] = BBU + 1, + [0x90] = BBR + 4, [0x95] = BBR + 7, + + /* Quadrants */ + [0x96] = BBQ + BL, + [0x97] = BBQ + BR, + [0x98] = BBQ + TL, + [0x99] = BBQ + TL + BL + BR, + [0x9a] = BBQ + TL + BR, + [0x9b] = BBQ + TL + TR + BL, + [0x9c] = BBQ + TL + TR + BR, + [0x9d] = BBQ + TR, + [0x9e] = BBQ + BL + TR, + [0x9f] = BBQ + BL + TR + BR, + + /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ + [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, + + /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ + /* U+2571 - U+2573: unsupported (diagonals) */ +}; \ No newline at end of file diff --git a/patch/copyurl.c b/patch/copyurl.c new file mode 100644 index 0000000..90d96db --- /dev/null +++ b/patch/copyurl.c @@ -0,0 +1,180 @@ +#if COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +void +tsetcolor( int row, int start, int end, uint32_t fg, uint32_t bg ) +{ + int i = start; + for( ; i < end; ++i ) + { + term.line[row][i].fg = fg; + term.line[row][i].bg = bg; + } +} + +char * +findlastany(char *str, const char** find, size_t len) +{ + char* found = NULL; + int i = 0; + for(found = str + strlen(str) - 1; found >= str; --found) { + for(i = 0; i < len; i++) { + if(strncmp(found, find[i], strlen(find[i])) == 0) { + return found; + } + } + } + + return NULL; +} + +/* +** Select and copy the previous url on screen (do nothing if there's no url). +** +** FIXME: doesn't handle urls that span multiple lines; will need to add support +** for multiline "getsel()" first +*/ +void +copyurl(const Arg *arg) { + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. + */ + static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + + static const char* URLSTRINGS[] = {"http://", "https://"}; + + /* remove highlighting from previous selection if any */ + if(sel.ob.x >= 0 && sel.oe.x >= 0) + tsetcolor(sel.nb.y, sel.ob.x, sel.oe.x + 1, defaultfg, defaultbg); + + int i = 0, + row = 0, /* row of current URL */ + col = 0, /* column of current URL start */ + startrow = 0, /* row of last occurrence */ + colend = 0, /* column of last occurrence */ + passes = 0; /* how many rows have been scanned */ + + char *linestr = calloc(term.col+1, sizeof(Rune)); + char *c = NULL, + *match = NULL; + + row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot; + LIMIT(row, term.top, term.bot); + startrow = row; + + colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col; + LIMIT(colend, 0, term.col); + + /* + ** Scan from (term.bot,term.col) to (0,0) and find + ** next occurrance of a URL + */ + while (passes !=term.bot + 2) { + /* Read in each column of every row until + ** we hit previous occurrence of URL + */ + for (col = 0, i = 0; col < colend; ++col,++i) { + linestr[i] = term.line[row][col].u; + } + linestr[term.col] = '\0'; + + if ((match = findlastany(linestr, URLSTRINGS, + sizeof(URLSTRINGS)/sizeof(URLSTRINGS[0])))) + break; + + if (--row < term.top) + row = term.bot; + + colend = term.col; + passes++; + }; + + if (match) { + /* must happen before trim */ + selclear(); + sel.ob.x = strlen(linestr) - strlen(match); + + /* trim the rest of the line from the url match */ + for (c = match; *c != '\0'; ++c) + if (!strchr(URLCHARS, *c)) { + *c = '\0'; + break; + } + + /* highlight selection by inverting terminal colors */ + tsetcolor(row, sel.ob.x, sel.ob.x + strlen( match ), defaultbg, defaultfg); + + /* select and copy */ + sel.mode = 1; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x + strlen(match)-1; + sel.ob.y = sel.oe.y = row; + selnormalize(); + tsetdirt(sel.nb.y, sel.ne.y); + xsetsel(getsel()); + xclipcopy(); + } + + free(linestr); +} +#else +/* select and copy the previous url on screen (do nothing if there's no url). + * known bug: doesn't handle urls that span multiple lines (wontfix), depends on multiline "getsel()" + * known bug: only finds first url on line (mightfix) + */ +void +copyurl(const Arg *arg) { + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. + */ + static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + + int i, row, startrow; + char *linestr = calloc(term.col+1, sizeof(Rune)); + char *c, *match = NULL; + + row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y-1 : term.bot; + LIMIT(row, term.top, term.bot); + startrow = row; + + /* find the start of the last url before selection */ + do { + for (i = 0; i < term.col; ++i) { + linestr[i] = term.line[row][i].u; + } + linestr[term.col] = '\0'; + if ((match = strstr(linestr, "http://")) + || (match = strstr(linestr, "https://"))) + break; + if (--row < term.top) + row = term.bot; + } while (row != startrow); + + if (match) { + /* must happen before trim */ + selclear(); + sel.ob.x = strlen(linestr) - strlen(match); + + /* trim the rest of the line from the url match */ + for (c = match; *c != '\0'; ++c) + if (!strchr(URLCHARS, *c)) { + *c = '\0'; + break; + } + + /* select and copy */ + sel.mode = 1; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x + strlen(match)-1; + sel.ob.y = sel.oe.y = row; + selnormalize(); + tsetdirt(sel.nb.y, sel.ne.y); + xsetsel(getsel()); + xclipcopy(); + } + + free(linestr); +} +#endif // COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH \ No newline at end of file diff --git a/patch/copyurl.h b/patch/copyurl.h new file mode 100644 index 0000000..88059af --- /dev/null +++ b/patch/copyurl.h @@ -0,0 +1,5 @@ +void copyurl(const Arg *); +#if COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +static void tsetcolor(int, int, int, uint32_t, uint32_t); +static char * findlastany(char *, const char**, size_t); +#endif // COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH \ No newline at end of file diff --git a/patch/externalpipe.c b/patch/externalpipe.c new file mode 100644 index 0000000..37d8848 --- /dev/null +++ b/patch/externalpipe.c @@ -0,0 +1,76 @@ +int extpipeactive = 0; + +void +#if EXTERNALPIPEIN_PATCH +extpipe(const Arg *arg, int in) +#else +externalpipe(const Arg *arg) +#endif // EXTERNALPIPEIN_PATCH +{ + int to[2]; + char buf[UTF_SIZ]; + void (*oldsigpipe)(int); + Glyph *bp, *end; + int lastpos, n, newline; + + if (pipe(to) == -1) + return; + + switch (fork()) { + case -1: + close(to[0]); + close(to[1]); + return; + case 0: + dup2(to[0], STDIN_FILENO); + close(to[0]); + close(to[1]); + #if EXTERNALPIPEIN_PATCH + if (in) + dup2(csdfd, STDOUT_FILENO); + close(csdfd); + #endif // EXTERNALPIPEIN_PATCH + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); + perror("failed"); + exit(0); + } + + close(to[0]); + /* ignore sigpipe for now, in case child exists early */ + oldsigpipe = signal(SIGPIPE, SIG_IGN); + newline = 0; + for (n = 0; n < term.row; n++) { + bp = term.line[n]; + lastpos = MIN(tlinelen(n) + 1, term.col) - 1; + if (lastpos < 0) + break; + end = &bp[lastpos + 1]; + for (; bp < end; ++bp) + if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) + break; + if ((newline = term.line[n][lastpos].mode & ATTR_WRAP)) + continue; + if (xwrite(to[1], "\n", 1) < 0) + break; + newline = 0; + } + if (newline) + (void)xwrite(to[1], "\n", 1); + close(to[1]); + /* restore */ + signal(SIGPIPE, oldsigpipe); + extpipeactive = 1; +} + +#if EXTERNALPIPEIN_PATCH +void +externalpipe(const Arg *arg) { + extpipe(arg, 0); +} + +void +externalpipein(const Arg *arg) { + extpipe(arg, 1); +} +#endif // EXTERNALPIPEIN_PATCH \ No newline at end of file diff --git a/patch/externalpipe.h b/patch/externalpipe.h new file mode 100644 index 0000000..764504b --- /dev/null +++ b/patch/externalpipe.h @@ -0,0 +1,4 @@ +void externalpipe(const Arg *); +#if EXTERNALPIPEIN_PATCH +void externalpipein(const Arg *); +#endif // EXTERNALPIPEIN_PATCH \ No newline at end of file diff --git a/patch/fixkeyboardinput.c b/patch/fixkeyboardinput.c new file mode 100644 index 0000000..75f8f40 --- /dev/null +++ b/patch/fixkeyboardinput.c @@ -0,0 +1,811 @@ +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { + XK_space, + XK_m, + XK_i, + XK_A, + XK_B, + XK_C, + XK_D, + XK_E, + XK_F, + XK_G, + XK_H, + XK_I, + XK_K, + XK_J, + XK_L, + XK_M, + XK_N, + XK_O, + XK_P, + XK_Q, + XK_R, + XK_S, + XK_T, + XK_U, + XK_V, + XK_W, + XK_X, + XK_Y, + XK_Z, + XK_0, + XK_1, + XK_2, + XK_3, + XK_4, + XK_5, + XK_6, + XK_7, + XK_8, + XK_9, + XK_exclam, + XK_quotedbl, + XK_numbersign, + XK_dollar, + XK_percent, + XK_ampersand, + XK_apostrophe, + XK_parenleft, + XK_parenright, + XK_asterisk, + XK_plus, + XK_comma, + XK_minus, + XK_period, + XK_slash, + XK_colon, + XK_semicolon, + XK_less, + XK_equal, + XK_greater, + XK_question, + XK_at, + XK_bracketleft, + XK_backslash, + XK_bracketright, + XK_asciicircum, + XK_underscore, + XK_grave, + XK_braceleft, + XK_bar, + XK_braceright, + XK_asciitilde, +}; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_NO_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, + + // libtermkey compatible keyboard input + { XK_KP_Home, XK_NO_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_NO_MOD, "\033[1~", 0, +1}, + { XK_KP_Home, ControlMask, "\033[149;5u", 0, 0}, + { XK_KP_Home, ControlMask|ShiftMask, "\033[149;6u", 0, 0}, + { XK_KP_Home, Mod1Mask, "\033[149;3u", 0, 0}, + { XK_KP_Home, Mod1Mask|ControlMask, "\033[149;7u", 0, 0}, + { XK_KP_Home, Mod1Mask|ControlMask|ShiftMask, "\033[149;8u", 0, 0}, + { XK_KP_Home, Mod1Mask|ShiftMask, "\033[149;4u", 0, 0}, + { XK_KP_Home, ShiftMask, "\033[149;2u", 0, 0}, + { XK_KP_Up, XK_NO_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_NO_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_NO_MOD, "\033OA", 0, +1}, + { XK_KP_Up, ControlMask, "\033[151;5u", 0, 0}, + { XK_KP_Up, ControlMask|ShiftMask, "\033[151;6u", 0, 0}, + { XK_KP_Up, Mod1Mask, "\033[151;3u", 0, 0}, + { XK_KP_Up, Mod1Mask|ControlMask, "\033[151;7u", 0, 0}, + { XK_KP_Up, Mod1Mask|ControlMask|ShiftMask, "\033[151;8u", 0, 0}, + { XK_KP_Up, Mod1Mask|ShiftMask, "\033[151;4u", 0, 0}, + { XK_KP_Up, ShiftMask, "\033[151;2u", 0, 0}, + { XK_KP_Down, XK_NO_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_NO_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_NO_MOD, "\033OB", 0, +1}, + { XK_KP_Down, ControlMask, "\033[153;5u", 0, 0}, + { XK_KP_Down, ControlMask|ShiftMask, "\033[153;6u", 0, 0}, + { XK_KP_Down, Mod1Mask, "\033[153;3u", 0, 0}, + { XK_KP_Down, Mod1Mask|ControlMask, "\033[153;7u", 0, 0}, + { XK_KP_Down, Mod1Mask|ControlMask|ShiftMask, "\033[153;8u", 0, 0}, + { XK_KP_Down, Mod1Mask|ShiftMask, "\033[153;4u", 0, 0}, + { XK_KP_Down, ShiftMask, "\033[153;2u", 0, 0}, + { XK_KP_Left, XK_NO_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_NO_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_NO_MOD, "\033OD", 0, +1}, + { XK_KP_Left, ControlMask, "\033[150;5u", 0, 0}, + { XK_KP_Left, ControlMask|ShiftMask, "\033[150;6u", 0, 0}, + { XK_KP_Left, Mod1Mask, "\033[150;3u", 0, 0}, + { XK_KP_Left, Mod1Mask|ControlMask, "\033[150;7u", 0, 0}, + { XK_KP_Left, Mod1Mask|ControlMask|ShiftMask, "\033[150;8u", 0, 0}, + { XK_KP_Left, Mod1Mask|ShiftMask, "\033[150;4u", 0, 0}, + { XK_KP_Left, ShiftMask, "\033[150;2u", 0, 0}, + { XK_KP_Right, XK_NO_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_NO_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_NO_MOD, "\033OC", 0, +1}, + { XK_KP_Right, ControlMask, "\033[152;5u", 0, 0}, + { XK_KP_Right, ControlMask|ShiftMask, "\033[152;6u", 0, 0}, + { XK_KP_Right, Mod1Mask, "\033[152;3u", 0, 0}, + { XK_KP_Right, Mod1Mask|ControlMask, "\033[152;7u", 0, 0}, + { XK_KP_Right, Mod1Mask|ControlMask|ShiftMask, "\033[152;8u", 0, 0}, + { XK_KP_Right, Mod1Mask|ShiftMask, "\033[152;4u", 0, 0}, + { XK_KP_Right, ShiftMask, "\033[152;2u", 0, 0}, + { XK_KP_Prior, XK_NO_MOD, "\033[5~", 0, 0}, + { XK_KP_Prior, ControlMask, "\033[154;5u", 0, 0}, + { XK_KP_Prior, ControlMask|ShiftMask, "\033[154;6u", 0, 0}, + { XK_KP_Prior, Mod1Mask, "\033[154;3u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ControlMask, "\033[154;7u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ControlMask|ShiftMask, "\033[154;8u", 0, 0}, + { XK_KP_Prior, Mod1Mask|ShiftMask, "\033[154;4u", 0, 0}, + { XK_KP_Begin, XK_NO_MOD, "\033[E", 0, 0}, + { XK_KP_Begin, ControlMask, "\033[157;5u", 0, 0}, + { XK_KP_Begin, ControlMask|ShiftMask, "\033[157;6u", 0, 0}, + { XK_KP_Begin, Mod1Mask, "\033[157;3u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ControlMask, "\033[157;7u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ControlMask|ShiftMask, "\033[157;8u", 0, 0}, + { XK_KP_Begin, Mod1Mask|ShiftMask, "\033[157;4u", 0, 0}, + { XK_KP_Begin, ShiftMask, "\033[157;2u", 0, 0}, + { XK_KP_End, XK_NO_MOD, "\033[4~", 0, 0}, + { XK_KP_End, ControlMask|ShiftMask, "\033[156;6u", 0, 0}, + { XK_KP_End, Mod1Mask, "\033[156;3u", 0, 0}, + { XK_KP_End, Mod1Mask|ControlMask, "\033[156;7u", 0, 0}, + { XK_KP_End, Mod1Mask|ControlMask|ShiftMask, "\033[156;8u", 0, 0}, + { XK_KP_End, Mod1Mask|ShiftMask, "\033[156;4u", 0, 0}, + { XK_KP_Next, XK_NO_MOD, "\033[6~", 0, 0}, + { XK_KP_Next, ControlMask, "\033[155;5u", 0, 0}, + { XK_KP_Next, ControlMask|ShiftMask, "\033[155;6u", 0, 0}, + { XK_KP_Next, Mod1Mask, "\033[155;3u", 0, 0}, + { XK_KP_Next, Mod1Mask|ControlMask, "\033[155;7u", 0, 0}, + { XK_KP_Next, Mod1Mask|ControlMask|ShiftMask, "\033[155;8u", 0, 0}, + { XK_KP_Next, Mod1Mask|ShiftMask, "\033[155;4u", 0, 0}, + { XK_KP_Insert, XK_NO_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_NO_MOD, "\033[2~", +1, 0}, + { XK_KP_Insert, ControlMask|ShiftMask, "\033[158;6u", 0, 0}, + { XK_KP_Insert, Mod1Mask, "\033[158;3u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ControlMask, "\033[158;7u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ControlMask|ShiftMask, "\033[158;8u", 0, 0}, + { XK_KP_Insert, Mod1Mask|ShiftMask, "\033[158;4u", 0, 0}, + { XK_KP_Delete, XK_NO_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_NO_MOD, "\033[3~", +1, 0}, + { XK_KP_Delete, ControlMask|ShiftMask, "\033[159;6u", 0, 0}, + { XK_KP_Delete, Mod1Mask, "\033[159;3u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ControlMask, "\033[159;7u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ControlMask|ShiftMask, "\033[159;8u", 0, 0}, + { XK_KP_Delete, Mod1Mask|ShiftMask, "\033[159;4u", 0, 0}, + { XK_KP_Multiply, XK_NO_MOD, "\033Oj", +2, 0}, + { XK_KP_Multiply, ControlMask, "\033[170;5u", 0, 0}, + { XK_KP_Multiply, ControlMask|ShiftMask, "\033[170;6u", 0, 0}, + { XK_KP_Multiply, Mod1Mask, "\033[170;3u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ControlMask, "\033[170;7u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ControlMask|ShiftMask, "\033[170;8u", 0, 0}, + { XK_KP_Multiply, Mod1Mask|ShiftMask, "\033[170;4u", 0, 0}, + { XK_KP_Multiply, ShiftMask, "\033[170;2u", 0, 0}, + { XK_KP_Add, XK_NO_MOD, "\033Ok", +2, 0}, + { XK_KP_Add, ControlMask, "\033[171;5u", 0, 0}, + { XK_KP_Add, ControlMask|ShiftMask, "\033[171;6u", 0, 0}, + { XK_KP_Add, Mod1Mask, "\033[171;3u", 0, 0}, + { XK_KP_Add, Mod1Mask|ControlMask, "\033[171;7u", 0, 0}, + { XK_KP_Add, Mod1Mask|ControlMask|ShiftMask, "\033[171;8u", 0, 0}, + { XK_KP_Add, Mod1Mask|ShiftMask, "\033[171;4u", 0, 0}, + { XK_KP_Add, ShiftMask, "\033[171;2u", 0, 0}, + { XK_KP_Enter, XK_NO_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_NO_MOD, "\r", -1, 0}, + { XK_KP_Enter, XK_NO_MOD, "\r\n", -1, 0}, + { XK_KP_Enter, ControlMask, "\033[141;5u", 0, 0}, + { XK_KP_Enter, ControlMask|ShiftMask, "\033[141;6u", 0, 0}, + { XK_KP_Enter, Mod1Mask, "\033[141;3u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ControlMask, "\033[141;7u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ControlMask|ShiftMask, "\033[141;8u", 0, 0}, + { XK_KP_Enter, Mod1Mask|ShiftMask, "\033[141;4u", 0, 0}, + { XK_KP_Enter, ShiftMask, "\033[141;2u", 0, 0}, + { XK_KP_Subtract, XK_NO_MOD, "\033Om", +2, 0}, + { XK_KP_Subtract, ControlMask, "\033[173;5u", 0, 0}, + { XK_KP_Subtract, ControlMask|ShiftMask, "\033[173;6u", 0, 0}, + { XK_KP_Subtract, Mod1Mask, "\033[173;3u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ControlMask, "\033[173;7u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ControlMask|ShiftMask, "\033[173;8u", 0, 0}, + { XK_KP_Subtract, Mod1Mask|ShiftMask, "\033[173;4u", 0, 0}, + { XK_KP_Subtract, ShiftMask, "\033[173;2u", 0, 0}, + { XK_KP_Decimal, XK_NO_MOD, "\033On", +2, 0}, + { XK_KP_Decimal, ControlMask, "\033[174;5u", 0, 0}, + { XK_KP_Decimal, ControlMask|ShiftMask, "\033[174;6u", 0, 0}, + { XK_KP_Decimal, Mod1Mask, "\033[174;3u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ControlMask, "\033[174;7u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ControlMask|ShiftMask, "\033[174;8u", 0, 0}, + { XK_KP_Decimal, Mod1Mask|ShiftMask, "\033[174;4u", 0, 0}, + { XK_KP_Decimal, ShiftMask, "\033[174;2u", 0, 0}, + { XK_KP_Divide, XK_NO_MOD, "\033Oo", +2, 0}, + { XK_KP_Divide, ControlMask, "\033[175;5u", 0, 0}, + { XK_KP_Divide, ControlMask|ShiftMask, "\033[175;6u", 0, 0}, + { XK_KP_Divide, Mod1Mask, "\033[175;3u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ControlMask, "\033[175;7u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ControlMask|ShiftMask, "\033[175;8u", 0, 0}, + { XK_KP_Divide, Mod1Mask|ShiftMask, "\033[175;4u", 0, 0}, + { XK_KP_Divide, ShiftMask, "\033[175;2u", 0, 0}, + { XK_KP_0, XK_NO_MOD, "\033Op", +2, 0}, + { XK_KP_0, ControlMask, "\033[176;5u", 0, 0}, + { XK_KP_0, ControlMask|ShiftMask, "\033[176;6u", 0, 0}, + { XK_KP_0, Mod1Mask, "\033[176;3u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask, "\033[176;7u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask|ShiftMask, "\033[176;8u", 0, 0}, + { XK_KP_0, Mod1Mask|ShiftMask, "\033[176;4u", 0, 0}, + { XK_KP_0, ShiftMask, "\033[176;2u", 0, 0}, + { XK_KP_1, XK_NO_MOD, "\033Oq", +2, 0}, + { XK_KP_0, ControlMask, "\033[177;5u", 0, 0}, + { XK_KP_0, ControlMask|ShiftMask, "\033[177;6u", 0, 0}, + { XK_KP_0, Mod1Mask, "\033[177;3u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask, "\033[177;7u", 0, 0}, + { XK_KP_0, Mod1Mask|ControlMask|ShiftMask, "\033[177;8u", 0, 0}, + { XK_KP_0, Mod1Mask|ShiftMask, "\033[177;4u", 0, 0}, + { XK_KP_0, ShiftMask, "\033[177;2u", 0, 0}, + { XK_KP_2, XK_NO_MOD, "\033Or", +2, 0}, + { XK_KP_2, ControlMask, "\033[178;5u", 0, 0}, + { XK_KP_2, ControlMask|ShiftMask, "\033[178;6u", 0, 0}, + { XK_KP_2, Mod1Mask, "\033[178;3u", 0, 0}, + { XK_KP_2, Mod1Mask|ControlMask, "\033[178;7u", 0, 0}, + { XK_KP_2, Mod1Mask|ControlMask|ShiftMask, "\033[178;8u", 0, 0}, + { XK_KP_2, Mod1Mask|ShiftMask, "\033[178;4u", 0, 0}, + { XK_KP_2, ShiftMask, "\033[178;2u", 0, 0}, + { XK_KP_3, XK_NO_MOD, "\033Os", +2, 0}, + { XK_KP_3, ControlMask, "\033[179;5u", 0, 0}, + { XK_KP_3, ControlMask|ShiftMask, "\033[179;6u", 0, 0}, + { XK_KP_3, Mod1Mask, "\033[179;3u", 0, 0}, + { XK_KP_3, Mod1Mask|ControlMask, "\033[179;7u", 0, 0}, + { XK_KP_3, Mod1Mask|ControlMask|ShiftMask, "\033[179;8u", 0, 0}, + { XK_KP_3, Mod1Mask|ShiftMask, "\033[179;4u", 0, 0}, + { XK_KP_3, ShiftMask, "\033[179;2u", 0, 0}, + { XK_KP_4, XK_NO_MOD, "\033Ot", +2, 0}, + { XK_KP_4, ControlMask, "\033[180;5u", 0, 0}, + { XK_KP_4, ControlMask|ShiftMask, "\033[180;6u", 0, 0}, + { XK_KP_4, Mod1Mask, "\033[180;3u", 0, 0}, + { XK_KP_4, Mod1Mask|ControlMask, "\033[180;7u", 0, 0}, + { XK_KP_4, Mod1Mask|ControlMask|ShiftMask, "\033[180;8u", 0, 0}, + { XK_KP_4, Mod1Mask|ShiftMask, "\033[180;4u", 0, 0}, + { XK_KP_4, ShiftMask, "\033[180;2u", 0, 0}, + { XK_KP_5, XK_NO_MOD, "\033Ou", +2, 0}, + { XK_KP_5, ControlMask, "\033[181;5u", 0, 0}, + { XK_KP_5, ControlMask|ShiftMask, "\033[181;6u", 0, 0}, + { XK_KP_5, Mod1Mask, "\033[181;3u", 0, 0}, + { XK_KP_5, Mod1Mask|ControlMask, "\033[181;7u", 0, 0}, + { XK_KP_5, Mod1Mask|ControlMask|ShiftMask, "\033[181;8u", 0, 0}, + { XK_KP_5, Mod1Mask|ShiftMask, "\033[181;4u", 0, 0}, + { XK_KP_5, ShiftMask, "\033[181;2u", 0, 0}, + { XK_KP_6, XK_NO_MOD, "\033Ov", +2, 0}, + { XK_KP_6, ControlMask, "\033[182;5u", 0, 0}, + { XK_KP_6, ControlMask|ShiftMask, "\033[182;6u", 0, 0}, + { XK_KP_6, Mod1Mask, "\033[182;3u", 0, 0}, + { XK_KP_6, Mod1Mask|ControlMask, "\033[182;7u", 0, 0}, + { XK_KP_6, Mod1Mask|ControlMask|ShiftMask, "\033[182;8u", 0, 0}, + { XK_KP_6, Mod1Mask|ShiftMask, "\033[182;4u", 0, 0}, + { XK_KP_6, ShiftMask, "\033[182;2u", 0, 0}, + { XK_KP_7, XK_NO_MOD, "\033Ow", +2, 0}, + { XK_KP_7, ControlMask, "\033[183;5u", 0, 0}, + { XK_KP_7, ControlMask|ShiftMask, "\033[183;6u", 0, 0}, + { XK_KP_7, Mod1Mask, "\033[183;3u", 0, 0}, + { XK_KP_7, Mod1Mask|ControlMask, "\033[183;7u", 0, 0}, + { XK_KP_7, Mod1Mask|ControlMask|ShiftMask, "\033[183;8u", 0, 0}, + { XK_KP_7, Mod1Mask|ShiftMask, "\033[183;4u", 0, 0}, + { XK_KP_7, ShiftMask, "\033[183;2u", 0, 0}, + { XK_KP_8, XK_NO_MOD, "\033Ox", +2, 0}, + { XK_KP_8, ControlMask, "\033[184;5u", 0, 0}, + { XK_KP_8, ControlMask|ShiftMask, "\033[184;6u", 0, 0}, + { XK_KP_8, Mod1Mask, "\033[184;3u", 0, 0}, + { XK_KP_8, Mod1Mask|ControlMask, "\033[184;7u", 0, 0}, + { XK_KP_8, Mod1Mask|ControlMask|ShiftMask, "\033[184;8u", 0, 0}, + { XK_KP_8, Mod1Mask|ShiftMask, "\033[184;4u", 0, 0}, + { XK_KP_8, ShiftMask, "\033[184;2u", 0, 0}, + { XK_KP_9, XK_NO_MOD, "\033Oy", +2, 0}, + { XK_KP_9, ControlMask, "\033[185;5u", 0, 0}, + { XK_KP_9, ControlMask|ShiftMask, "\033[185;6u", 0, 0}, + { XK_KP_9, Mod1Mask, "\033[185;3u", 0, 0}, + { XK_KP_9, Mod1Mask|ControlMask, "\033[185;7u", 0, 0}, + { XK_KP_9, Mod1Mask|ControlMask|ShiftMask, "\033[185;8u", 0, 0}, + { XK_KP_9, Mod1Mask|ShiftMask, "\033[185;4u", 0, 0}, + { XK_KP_9, ShiftMask, "\033[185;2u", 0, 0}, + { XK_BackSpace, ControlMask, "\033[127;5u", 0, 0}, + { XK_BackSpace, ControlMask|ShiftMask, "\033[127;6u", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033[127;3u", 0, 0}, + { XK_BackSpace, Mod1Mask|ControlMask, "\033[127;7u", 0, 0}, + { XK_BackSpace, Mod1Mask|ControlMask|ShiftMask, "\033[127;8u", 0, 0}, + { XK_BackSpace, Mod1Mask|ShiftMask, "\033[127;4u", 0, 0}, + { XK_BackSpace, ShiftMask, "\033[127;2u", 0, 0}, + { XK_Tab, ControlMask, "\033[9;5u", 0, 0}, + { XK_Tab, ControlMask|ShiftMask, "\033[1;5Z", 0, 0}, + { XK_Tab, Mod1Mask, "\033[1;3Z", 0, 0}, + { XK_Tab, Mod1Mask|ControlMask, "\033[1;7Z", 0, 0}, + { XK_Tab, Mod1Mask|ControlMask|ShiftMask, "\033[1;8Z", 0, 0}, + { XK_Tab, Mod1Mask|ShiftMask, "\033[1;4Z", 0, 0}, + { XK_Return, ControlMask, "\033[13;5u", 0, 0}, + { XK_Return, ControlMask|ShiftMask, "\033[13;6u", 0, 0}, + { XK_Return, Mod1Mask, "\033[13;3u", 0, 0}, + { XK_Return, Mod1Mask|ControlMask, "\033[13;7u", 0, 0}, + { XK_Return, Mod1Mask|ControlMask|ShiftMask, "\033[13;8u", 0, 0}, + { XK_Return, Mod1Mask|ShiftMask, "\033[13;4u", 0, 0}, + { XK_Return, ShiftMask, "\033[13;2u", 0, 0}, + { XK_Pause, ControlMask, "\033[18;5u", 0, 0}, + { XK_Pause, ControlMask|ShiftMask, "\033[18;6u", 0, 0}, + { XK_Pause, Mod1Mask, "\033[18;3u", 0, 0}, + { XK_Pause, Mod1Mask|ControlMask, "\033[18;7u", 0, 0}, + { XK_Pause, Mod1Mask|ControlMask|ShiftMask, "\033[18;8u", 0, 0}, + { XK_Pause, Mod1Mask|ShiftMask, "\033[18;4u", 0, 0}, + { XK_Pause, ShiftMask, "\033[18;2u", 0, 0}, + { XK_Scroll_Lock, ControlMask, "\033[20;5u", 0, 0}, + { XK_Scroll_Lock, ControlMask|ShiftMask, "\033[20;6u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask, "\033[20;3u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ControlMask, "\033[20;7u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ControlMask|ShiftMask, "\033[20;8u", 0, 0}, + { XK_Scroll_Lock, Mod1Mask|ShiftMask, "\033[20;4u", 0, 0}, + { XK_Scroll_Lock, ShiftMask, "\033[20;2u", 0, 0}, + { XK_Escape, ControlMask, "\033[27;5u", 0, 0}, + { XK_Escape, ControlMask|ShiftMask, "\033[27;6u", 0, 0}, + { XK_Escape, Mod1Mask, "\033[27;3u", 0, 0}, + { XK_Escape, Mod1Mask|ControlMask, "\033[27;7u", 0, 0}, + { XK_Escape, Mod1Mask|ControlMask|ShiftMask, "\033[27;8u", 0, 0}, + { XK_Escape, Mod1Mask|ShiftMask, "\033[27;4u", 0, 0}, + { XK_Escape, ShiftMask, "\033[27;2u", 0, 0}, + { XK_Home, XK_NO_MOD, "\033[H", 0, -1}, + { XK_Home, XK_NO_MOD, "\033[1~", 0, +1}, + { XK_Home, ControlMask|ShiftMask, "\033[80;6u", 0, 0}, + { XK_Home, Mod1Mask, "\033[80;3u", 0, 0}, + { XK_Home, Mod1Mask|ControlMask, "\033[80;7u", 0, 0}, + { XK_Home, Mod1Mask|ControlMask|ShiftMask, "\033[80;8u", 0, 0}, + { XK_Home, Mod1Mask|ShiftMask, "\033[80;4u", 0, 0}, + { XK_End, XK_NO_MOD, "\033[4~", 0, 0}, + { XK_End, ControlMask|ShiftMask, "\033[87;6u", 0, 0}, + { XK_End, Mod1Mask, "\033[87;3u", 0, 0}, + { XK_End, Mod1Mask|ControlMask, "\033[87;7u", 0, 0}, + { XK_End, Mod1Mask|ControlMask|ShiftMask, "\033[87;8u", 0, 0}, + { XK_End, Mod1Mask|ShiftMask, "\033[87;4u", 0, 0}, + { XK_Prior, XK_NO_MOD, "\033[5~", 0, 0}, + { XK_Prior, ControlMask|ShiftMask, "\033[85;6u", 0, 0}, + { XK_Prior, Mod1Mask, "\033[85;3u", 0, 0}, + { XK_Prior, Mod1Mask|ControlMask, "\033[85;7u", 0, 0}, + { XK_Prior, Mod1Mask|ControlMask|ShiftMask, "\033[85;8u", 0, 0}, + { XK_Prior, Mod1Mask|ShiftMask, "\033[85;4u", 0, 0}, + { XK_Next, XK_NO_MOD, "\033[6~", 0, 0}, + { XK_Next, ControlMask|ShiftMask, "\033[86;6u", 0, 0}, + { XK_Next, Mod1Mask, "\033[86;3u", 0, 0}, + { XK_Next, Mod1Mask|ControlMask, "\033[86;7u", 0, 0}, + { XK_Next, Mod1Mask|ControlMask|ShiftMask, "\033[86;8u", 0, 0}, + { XK_Next, Mod1Mask|ShiftMask, "\033[86;4u", 0, 0}, + { XK_Print, ControlMask, "\033[97;5u", 0, 0}, + { XK_Print, ControlMask|ShiftMask, "\033[97;6u", 0, 0}, + { XK_Print, Mod1Mask, "\033[97;3u", 0, 0}, + { XK_Print, Mod1Mask|ControlMask, "\033[97;7u", 0, 0}, + { XK_Print, Mod1Mask|ControlMask|ShiftMask, "\033[97;8u", 0, 0}, + { XK_Print, Mod1Mask|ShiftMask, "\033[97;4u", 0, 0}, + { XK_Print, ShiftMask, "\033[97;2u", 0, 0}, + { XK_Insert, XK_NO_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_NO_MOD, "\033[2~", +1, 0}, + { XK_Insert, ControlMask|ShiftMask, "\033[99;6u", 0, 0}, + { XK_Insert, Mod1Mask, "\033[99;3u", 0, 0}, + { XK_Insert, Mod1Mask|ControlMask, "\033[99;7u", 0, 0}, + { XK_Insert, Mod1Mask|ControlMask|ShiftMask, "\033[99;8u", 0, 0}, + { XK_Insert, Mod1Mask|ShiftMask, "\033[99;4u", 0, 0}, + { XK_Menu, ControlMask, "\033[103;5u", 0, 0}, + { XK_Menu, ControlMask|ShiftMask, "\033[103;6u", 0, 0}, + { XK_Menu, Mod1Mask, "\033[103;3u", 0, 0}, + { XK_Menu, Mod1Mask|ControlMask, "\033[103;7u", 0, 0}, + { XK_Menu, Mod1Mask|ControlMask|ShiftMask, "\033[103;8u", 0, 0}, + { XK_Menu, Mod1Mask|ShiftMask, "\033[103;4u", 0, 0}, + { XK_Menu, ShiftMask, "\033[103;2u", 0, 0}, + { XK_Delete, XK_NO_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_NO_MOD, "\033[3~", +1, 0}, + { XK_Delete, ControlMask|ShiftMask, "\033[255;6u", 0, 0}, + { XK_Delete, Mod1Mask, "\033[255;3u", 0, 0}, + { XK_Delete, Mod1Mask|ControlMask, "\033[255;7u", 0, 0}, + { XK_Delete, Mod1Mask|ControlMask|ShiftMask, "\033[255;8u", 0, 0}, + { XK_Delete, Mod1Mask|ShiftMask, "\033[255;4u", 0, 0}, + { XK_i, ControlMask, "\033[105;5u", 0, 0}, + { XK_i, Mod1Mask|ControlMask, "\033[105;7u", 0, 0}, + { XK_m, ControlMask, "\033[109;5u", 0, 0}, + { XK_m, Mod1Mask|ControlMask, "\033[109;7u", 0, 0}, + { XK_space, ControlMask|ShiftMask, "\033[32;6u", 0, 0}, + { XK_space, Mod1Mask, "\033[32;3u", 0, 0}, + { XK_space, Mod1Mask|ControlMask, "\033[32;7u", 0, 0}, + { XK_space, Mod1Mask|ControlMask|ShiftMask, "\033[32;8u", 0, 0}, + { XK_space, Mod1Mask|ShiftMask, "\033[32;4u", 0, 0}, + { XK_space, ShiftMask, "\033[32;2u", 0, 0}, + { XK_0, ControlMask, "\033[48;5u", 0, 0}, + { XK_A, ControlMask|ShiftMask, "\033[65;6u", 0, 0}, + { XK_B, ControlMask|ShiftMask, "\033[66;6u", 0, 0}, + { XK_C, ControlMask|ShiftMask, "\033[67;6u", 0, 0}, + { XK_D, ControlMask|ShiftMask, "\033[68;6u", 0, 0}, + { XK_E, ControlMask|ShiftMask, "\033[69;6u", 0, 0}, + { XK_F, ControlMask|ShiftMask, "\033[70;6u", 0, 0}, + { XK_G, ControlMask|ShiftMask, "\033[71;6u", 0, 0}, + { XK_H, ControlMask|ShiftMask, "\033[72;6u", 0, 0}, + { XK_I, ControlMask|ShiftMask, "\033[73;6u", 0, 0}, + { XK_I, Mod1Mask|ControlMask|ShiftMask, "\033[73;8u", 0, 0}, + { XK_J, ControlMask|ShiftMask, "\033[75;6u", 0, 0}, + { XK_K, ControlMask|ShiftMask, "\033[74;6u", 0, 0}, + { XK_L, ControlMask|ShiftMask, "\033[76;6u", 0, 0}, + { XK_M, ControlMask|ShiftMask, "\033[77;6u", 0, 0}, + { XK_M, Mod1Mask|ControlMask|ShiftMask, "\033[77;8u", 0, 0}, + { XK_N, ControlMask|ShiftMask, "\033[78;6u", 0, 0}, + { XK_O, ControlMask|ShiftMask, "\033[79;6u", 0, 0}, + { XK_P, ControlMask|ShiftMask, "\033[80;6u", 0, 0}, + { XK_Q, ControlMask|ShiftMask, "\033[81;6u", 0, 0}, + { XK_R, ControlMask|ShiftMask, "\033[82;6u", 0, 0}, + { XK_S, ControlMask|ShiftMask, "\033[83;6u", 0, 0}, + { XK_T, ControlMask|ShiftMask, "\033[84;6u", 0, 0}, + { XK_U, ControlMask|ShiftMask, "\033[85;6u", 0, 0}, + { XK_V, ControlMask|ShiftMask, "\033[86;6u", 0, 0}, + { XK_W, ControlMask|ShiftMask, "\033[87;6u", 0, 0}, + { XK_X, ControlMask|ShiftMask, "\033[88;6u", 0, 0}, + { XK_Y, ControlMask|ShiftMask, "\033[89;6u", 0, 0}, + { XK_Z, ControlMask|ShiftMask, "\033[90;6u", 0, 0}, + { XK_0, Mod1Mask|ControlMask, "\033[48;7u", 0, 0}, + { XK_1, ControlMask, "\033[49;5u", 0, 0}, + { XK_1, Mod1Mask|ControlMask, "\033[49;7u", 0, 0}, + { XK_2, ControlMask, "\033[50;5u", 0, 0}, + { XK_2, Mod1Mask|ControlMask, "\033[50;7u", 0, 0}, + { XK_3, ControlMask, "\033[51;5u", 0, 0}, + { XK_3, Mod1Mask|ControlMask, "\033[51;7u", 0, 0}, + { XK_4, ControlMask, "\033[52;5u", 0, 0}, + { XK_4, Mod1Mask|ControlMask, "\033[52;7u", 0, 0}, + { XK_5, ControlMask, "\033[53;5u", 0, 0}, + { XK_5, Mod1Mask|ControlMask, "\033[53;7u", 0, 0}, + { XK_6, ControlMask, "\033[54;5u", 0, 0}, + { XK_6, Mod1Mask|ControlMask, "\033[54;7u", 0, 0}, + { XK_7, ControlMask, "\033[55;5u", 0, 0}, + { XK_7, Mod1Mask|ControlMask, "\033[55;7u", 0, 0}, + { XK_8, ControlMask, "\033[56;5u", 0, 0}, + { XK_8, Mod1Mask|ControlMask, "\033[56;7u", 0, 0}, + { XK_9, ControlMask, "\033[57;5u", 0, 0}, + { XK_9, Mod1Mask|ControlMask, "\033[57;7u", 0, 0}, + { XK_ampersand, ControlMask, "\033[38;5u", 0, 0}, + { XK_ampersand, ControlMask|ShiftMask, "\033[38;6u", 0, 0}, + { XK_ampersand, Mod1Mask, "\033[38;3u", 0, 0}, + { XK_ampersand, Mod1Mask|ControlMask, "\033[38;7u", 0, 0}, + { XK_ampersand, Mod1Mask|ControlMask|ShiftMask, "\033[38;8u", 0, 0}, + { XK_ampersand, Mod1Mask|ShiftMask, "\033[38;4u", 0, 0}, + { XK_apostrophe, ControlMask, "\033[39;5u", 0, 0}, + { XK_apostrophe, ControlMask|ShiftMask, "\033[39;6u", 0, 0}, + { XK_apostrophe, Mod1Mask, "\033[39;3u", 0, 0}, + { XK_apostrophe, Mod1Mask|ControlMask, "\033[39;7u", 0, 0}, + { XK_apostrophe, Mod1Mask|ControlMask|ShiftMask, "\033[39;8u", 0, 0}, + { XK_apostrophe, Mod1Mask|ShiftMask, "\033[39;4u", 0, 0}, + { XK_asciicircum, ControlMask, "\033[94;5u", 0, 0}, + { XK_asciicircum, ControlMask|ShiftMask, "\033[94;6u", 0, 0}, + { XK_asciicircum, Mod1Mask, "\033[94;3u", 0, 0}, + { XK_asciicircum, Mod1Mask|ControlMask, "\033[94;7u", 0, 0}, + { XK_asciicircum, Mod1Mask|ControlMask|ShiftMask, "\033[94;8u", 0, 0}, + { XK_asciicircum, Mod1Mask|ShiftMask, "\033[94;4u", 0, 0}, + { XK_asciitilde, ControlMask, "\033[126;5u", 0, 0}, + { XK_asciitilde, ControlMask|ShiftMask, "\033[126;6u", 0, 0}, + { XK_asciitilde, Mod1Mask, "\033[126;3u", 0, 0}, + { XK_asciitilde, Mod1Mask|ControlMask, "\033[126;7u", 0, 0}, + { XK_asciitilde, Mod1Mask|ControlMask|ShiftMask, "\033[126;8u", 0, 0}, + { XK_asciitilde, Mod1Mask|ShiftMask, "\033[126;4u", 0, 0}, + { XK_asterisk, ControlMask, "\033[42;5u", 0, 0}, + { XK_asterisk, ControlMask|ShiftMask, "\033[42;6u", 0, 0}, + { XK_asterisk, Mod1Mask, "\033[42;3u", 0, 0}, + { XK_asterisk, Mod1Mask|ControlMask, "\033[42;7u", 0, 0}, + { XK_asterisk, Mod1Mask|ControlMask|ShiftMask, "\033[42;8u", 0, 0}, + { XK_asterisk, Mod1Mask|ShiftMask, "\033[42;4u", 0, 0}, + { XK_at, ControlMask, "\033[64;5u", 0, 0}, + { XK_at, ControlMask|ShiftMask, "\033[64;6u", 0, 0}, + { XK_at, Mod1Mask, "\033[64;3u", 0, 0}, + { XK_at, Mod1Mask|ControlMask, "\033[64;7u", 0, 0}, + { XK_at, Mod1Mask|ControlMask|ShiftMask, "\033[64;8u", 0, 0}, + { XK_at, Mod1Mask|ShiftMask, "\033[64;4u", 0, 0}, + { XK_backslash, ControlMask, "\033[92;5u", 0, 0}, + { XK_backslash, ControlMask|ShiftMask, "\033[92;6u", 0, 0}, + { XK_backslash, Mod1Mask, "\033[92;3u", 0, 0}, + { XK_backslash, Mod1Mask|ControlMask, "\033[92;7u", 0, 0}, + { XK_backslash, Mod1Mask|ControlMask|ShiftMask, "\033[92;8u", 0, 0}, + { XK_backslash, Mod1Mask|ShiftMask, "\033[92;4u", 0, 0}, + { XK_bar, ControlMask, "\033[124;5u", 0, 0}, + { XK_bar, ControlMask|ShiftMask, "\033[124;6u", 0, 0}, + { XK_bar, Mod1Mask, "\033[124;3u", 0, 0}, + { XK_bar, Mod1Mask|ControlMask, "\033[124;7u", 0, 0}, + { XK_bar, Mod1Mask|ControlMask|ShiftMask, "\033[124;8u", 0, 0}, + { XK_bar, Mod1Mask|ShiftMask, "\033[124;4u", 0, 0}, + { XK_braceleft, ControlMask, "\033[123;5u", 0, 0}, + { XK_braceleft, ControlMask|ShiftMask, "\033[123;6u", 0, 0}, + { XK_braceleft, Mod1Mask, "\033[123;3u", 0, 0}, + { XK_braceleft, Mod1Mask|ControlMask, "\033[123;7u", 0, 0}, + { XK_braceleft, Mod1Mask|ControlMask|ShiftMask, "\033[123;8u", 0, 0}, + { XK_braceleft, Mod1Mask|ShiftMask, "\033[123;4u", 0, 0}, + { XK_braceright, ControlMask, "\033[125;5u", 0, 0}, + { XK_braceright, ControlMask|ShiftMask, "\033[125;6u", 0, 0}, + { XK_braceright, Mod1Mask, "\033[125;3u", 0, 0}, + { XK_braceright, Mod1Mask|ControlMask, "\033[125;7u", 0, 0}, + { XK_braceright, Mod1Mask|ControlMask|ShiftMask, "\033[125;8u", 0, 0}, + { XK_braceright, Mod1Mask|ShiftMask, "\033[125;4u", 0, 0}, + { XK_bracketleft, ControlMask, "\033[91;5u", 0, 0}, + { XK_bracketleft, ControlMask|ShiftMask, "\033[91;6u", 0, 0}, + { XK_bracketleft, Mod1Mask, "\033[91;3u", 0, 0}, + { XK_bracketleft, Mod1Mask|ControlMask, "\033[91;7u", 0, 0}, + { XK_bracketleft, Mod1Mask|ControlMask|ShiftMask, "\033[91;8u", 0, 0}, + { XK_bracketleft, Mod1Mask|ShiftMask, "\033[91;4u", 0, 0}, + { XK_bracketright, ControlMask, "\033[93;5u", 0, 0}, + { XK_bracketright, ControlMask|ShiftMask, "\033[93;6u", 0, 0}, + { XK_bracketright, Mod1Mask, "\033[93;3u", 0, 0}, + { XK_bracketright, Mod1Mask|ControlMask, "\033[93;7u", 0, 0}, + { XK_bracketright, Mod1Mask|ControlMask|ShiftMask, "\033[93;8u", 0, 0}, + { XK_bracketright, Mod1Mask|ShiftMask, "\033[93;4u", 0, 0}, + { XK_colon, ControlMask, "\033[58;5u", 0, 0}, + { XK_colon, ControlMask|ShiftMask, "\033[58;6u", 0, 0}, + { XK_colon, Mod1Mask, "\033[58;3u", 0, 0}, + { XK_colon, Mod1Mask|ControlMask, "\033[58;7u", 0, 0}, + { XK_colon, Mod1Mask|ControlMask|ShiftMask, "\033[58;8u", 0, 0}, + { XK_colon, Mod1Mask|ShiftMask, "\033[58;4u", 0, 0}, + { XK_comma, ControlMask, "\033[44;5u", 0, 0}, + { XK_comma, ControlMask|ShiftMask, "\033[44;6u", 0, 0}, + { XK_comma, Mod1Mask, "\033[44;3u", 0, 0}, + { XK_comma, Mod1Mask|ControlMask, "\033[44;7u", 0, 0}, + { XK_comma, Mod1Mask|ControlMask|ShiftMask, "\033[44;8u", 0, 0}, + { XK_comma, Mod1Mask|ShiftMask, "\033[44;4u", 0, 0}, + { XK_dollar, ControlMask, "\033[36;5u", 0, 0}, + { XK_dollar, ControlMask|ShiftMask, "\033[36;6u", 0, 0}, + { XK_dollar, Mod1Mask, "\033[36;3u", 0, 0}, + { XK_dollar, Mod1Mask|ControlMask, "\033[36;7u", 0, 0}, + { XK_dollar, Mod1Mask|ControlMask|ShiftMask, "\033[36;8u", 0, 0}, + { XK_dollar, Mod1Mask|ShiftMask, "\033[36;4u", 0, 0}, + { XK_equal, ControlMask, "\033[61;5u", 0, 0}, + { XK_equal, ControlMask|ShiftMask, "\033[61;6u", 0, 0}, + { XK_equal, Mod1Mask, "\033[61;3u", 0, 0}, + { XK_equal, Mod1Mask|ControlMask, "\033[61;7u", 0, 0}, + { XK_equal, Mod1Mask|ControlMask|ShiftMask, "\033[61;8u", 0, 0}, + { XK_equal, Mod1Mask|ShiftMask, "\033[61;4u", 0, 0}, + { XK_exclam, ControlMask, "\033[33;5u", 0, 0}, + { XK_exclam, ControlMask|ShiftMask, "\033[33;6u", 0, 0}, + { XK_exclam, Mod1Mask, "\033[33;3u", 0, 0}, + { XK_exclam, Mod1Mask|ControlMask, "\033[33;7u", 0, 0}, + { XK_exclam, Mod1Mask|ControlMask|ShiftMask, "\033[33;8u", 0, 0}, + { XK_exclam, Mod1Mask|ShiftMask, "\033[33;4u", 0, 0}, + { XK_grave, ControlMask, "\033[96;5u", 0, 0}, + { XK_grave, ControlMask|ShiftMask, "\033[96;6u", 0, 0}, + { XK_grave, Mod1Mask, "\033[96;3u", 0, 0}, + { XK_grave, Mod1Mask|ControlMask, "\033[96;7u", 0, 0}, + { XK_grave, Mod1Mask|ControlMask|ShiftMask, "\033[96;8u", 0, 0}, + { XK_grave, Mod1Mask|ShiftMask, "\033[96;4u", 0, 0}, + { XK_greater, ControlMask, "\033[62;5u", 0, 0}, + { XK_greater, ControlMask|ShiftMask, "\033[62;6u", 0, 0}, + { XK_greater, Mod1Mask, "\033[62;3u", 0, 0}, + { XK_greater, Mod1Mask|ControlMask, "\033[62;7u", 0, 0}, + { XK_greater, Mod1Mask|ControlMask|ShiftMask, "\033[62;8u", 0, 0}, + { XK_greater, Mod1Mask|ShiftMask, "\033[62;4u", 0, 0}, + { XK_less, ControlMask, "\033[60;5u", 0, 0}, + { XK_less, ControlMask|ShiftMask, "\033[60;6u", 0, 0}, + { XK_less, Mod1Mask, "\033[60;3u", 0, 0}, + { XK_less, Mod1Mask|ControlMask, "\033[60;7u", 0, 0}, + { XK_less, Mod1Mask|ControlMask|ShiftMask, "\033[60;8u", 0, 0}, + { XK_less, Mod1Mask|ShiftMask, "\033[60;4u", 0, 0}, + { XK_minus, ControlMask, "\033[45;5u", 0, 0}, + { XK_minus, ControlMask|ShiftMask, "\033[45;6u", 0, 0}, + { XK_minus, Mod1Mask, "\033[45;3u", 0, 0}, + { XK_minus, Mod1Mask|ControlMask, "\033[45;7u", 0, 0}, + { XK_minus, Mod1Mask|ControlMask|ShiftMask, "\033[45;8u", 0, 0}, + { XK_minus, Mod1Mask|ShiftMask, "\033[45;4u", 0, 0}, + { XK_numbersign, ControlMask, "\033[35;5u", 0, 0}, + { XK_numbersign, ControlMask|ShiftMask, "\033[35;6u", 0, 0}, + { XK_numbersign, Mod1Mask, "\033[35;3u", 0, 0}, + { XK_numbersign, Mod1Mask|ControlMask, "\033[35;7u", 0, 0}, + { XK_numbersign, Mod1Mask|ControlMask|ShiftMask, "\033[35;8u", 0, 0}, + { XK_numbersign, Mod1Mask|ShiftMask, "\033[35;4u", 0, 0}, + { XK_parenleft, ControlMask, "\033[40;5u", 0, 0}, + { XK_parenleft, ControlMask|ShiftMask, "\033[40;6u", 0, 0}, + { XK_parenleft, Mod1Mask, "\033[40;3u", 0, 0}, + { XK_parenleft, Mod1Mask|ControlMask, "\033[40;7u", 0, 0}, + { XK_parenleft, Mod1Mask|ControlMask|ShiftMask, "\033[40;8u", 0, 0}, + { XK_parenleft, Mod1Mask|ShiftMask, "\033[40;4u", 0, 0}, + { XK_parenright, ControlMask, "\033[41;5u", 0, 0}, + { XK_parenright, ControlMask|ShiftMask, "\033[41;6u", 0, 0}, + { XK_parenright, Mod1Mask, "\033[41;3u", 0, 0}, + { XK_parenright, Mod1Mask|ControlMask, "\033[41;7u", 0, 0}, + { XK_parenright, Mod1Mask|ControlMask|ShiftMask, "\033[41;8u", 0, 0}, + { XK_parenright, Mod1Mask|ShiftMask, "\033[41;4u", 0, 0}, + { XK_percent, ControlMask, "\033[37;5u", 0, 0}, + { XK_percent, ControlMask|ShiftMask, "\033[37;6u", 0, 0}, + { XK_percent, Mod1Mask, "\033[37;3u", 0, 0}, + { XK_percent, Mod1Mask|ControlMask, "\033[37;7u", 0, 0}, + { XK_percent, Mod1Mask|ControlMask|ShiftMask, "\033[37;8u", 0, 0}, + { XK_percent, Mod1Mask|ShiftMask, "\033[37;4u", 0, 0}, + { XK_period, ControlMask, "\033[46;5u", 0, 0}, + { XK_period, ControlMask|ShiftMask, "\033[46;6u", 0, 0}, + { XK_period, Mod1Mask|ControlMask, "\033[46;7u", 0, 0}, + { XK_period, Mod1Mask|ControlMask|ShiftMask, "\033[46;8u", 0, 0}, + { XK_period, Mod1Mask|ShiftMask, "\033[46;4u", 0, 0}, + { XK_plus, ControlMask, "\033[43;5u", 0, 0}, + { XK_plus, ControlMask|ShiftMask, "\033[43;6u", 0, 0}, + { XK_plus, Mod1Mask, "\033[43;3u", 0, 0}, + { XK_plus, Mod1Mask|ControlMask, "\033[43;7u", 0, 0}, + { XK_plus, Mod1Mask|ControlMask|ShiftMask, "\033[43;8u", 0, 0}, + { XK_plus, Mod1Mask|ShiftMask, "\033[43;4u", 0, 0}, + { XK_question, ControlMask, "\033[63;5u", 0, 0}, + { XK_question, ControlMask|ShiftMask, "\033[63;6u", 0, 0}, + { XK_question, Mod1Mask, "\033[63;3u", 0, 0}, + { XK_question, Mod1Mask|ControlMask, "\033[63;7u", 0, 0}, + { XK_question, Mod1Mask|ControlMask|ShiftMask, "\033[63;8u", 0, 0}, + { XK_question, Mod1Mask|ShiftMask, "\033[63;4u", 0, 0}, + { XK_quotedbl, ControlMask, "\033[34;5u", 0, 0}, + { XK_quotedbl, ControlMask|ShiftMask, "\033[34;6u", 0, 0}, + { XK_quotedbl, Mod1Mask, "\033[34;3u", 0, 0}, + { XK_quotedbl, Mod1Mask|ControlMask, "\033[34;7u", 0, 0}, + { XK_quotedbl, Mod1Mask|ControlMask|ShiftMask, "\033[34;8u", 0, 0}, + { XK_quotedbl, Mod1Mask|ShiftMask, "\033[34;4u", 0, 0}, + { XK_semicolon, ControlMask, "\033[59;5u", 0, 0}, + { XK_semicolon, ControlMask|ShiftMask, "\033[59;6u", 0, 0}, + { XK_semicolon, Mod1Mask, "\033[59;3u", 0, 0}, + { XK_semicolon, Mod1Mask|ControlMask, "\033[59;7u", 0, 0}, + { XK_semicolon, Mod1Mask|ControlMask|ShiftMask, "\033[59;8u", 0, 0}, + { XK_semicolon, Mod1Mask|ShiftMask, "\033[59;4u", 0, 0}, + { XK_slash, ControlMask|ShiftMask, "\033[47;6u", 0, 0}, + { XK_slash, Mod1Mask, "\033[47;3u", 0, 0}, + { XK_slash, Mod1Mask|ControlMask, "\033[47;7u", 0, 0}, + { XK_slash, Mod1Mask|ControlMask|ShiftMask, "\033[47;8u", 0, 0}, + { XK_slash, Mod1Mask|ShiftMask, "\033[47;4u", 0, 0}, + { XK_underscore, ControlMask, "\033[95;5u", 0, 0}, + { XK_underscore, ControlMask|ShiftMask, "\033[95;6u", 0, 0}, + { XK_underscore, Mod1Mask, "\033[95;3u", 0, 0}, + { XK_underscore, Mod1Mask|ControlMask, "\033[95;7u", 0, 0}, + { XK_underscore, Mod1Mask|ControlMask|ShiftMask, "\033[95;8u", 0, 0}, + { XK_underscore, Mod1Mask|ShiftMask, "\033[95;4u", 0, 0}, +}; \ No newline at end of file diff --git a/patch/font2.c b/patch/font2.c new file mode 100644 index 0000000..9fc0e2d --- /dev/null +++ b/patch/font2.c @@ -0,0 +1,104 @@ +int +xloadsparefont(FcPattern *pattern, int flags) +{ + FcPattern *match; + FcResult result; + + #if USE_XFTFONTMATCH_PATCH + match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); + #else + match = FcFontMatch(NULL, pattern, &result); + #endif // USE_XFTFONTMATCH_PATCH + if (!match) { + return 1; + } + + if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(match); + return 1; + } + + frc[frclen].flags = flags; + /* Believe U+0000 glyph will present in each default font */ + frc[frclen].unicodep = 0; + frclen++; + + return 0; +} + +void +xloadsparefonts(void) +{ + FcPattern *pattern; + double fontval; + int fc; + char **fp; + + if (frclen != 0) + die("can't embed spare fonts. cache isn't empty"); + + /* Calculate count of spare fonts */ + fc = sizeof(font2) / sizeof(*font2); + if (fc == 0) + return; + + /* Allocate memory for cache entries. */ + if (frccap < 4 * fc) { + frccap += 4 * fc - frccap; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + for (fp = font2; fp - font2 < fc; ++fp) { + + if (**fp == '-') + pattern = XftXlfdParse(*fp, False, False); + else + pattern = FcNameParse((FcChar8 *)*fp); + + if (!pattern) + die("can't open spare font %s\n", *fp); + + if (defaultfontsize > 0 && defaultfontsize != usedfontsize) { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + fontval *= usedfontsize / defaultfontsize; + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + fontval *= usedfontsize / defaultfontsize; + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_SIZE, fontval); + } + } + + FcPatternAddBool(pattern, FC_SCALABLE, 1); + + #if !USE_XFTFONTMATCH_PATCH + FcConfigSubstitute(NULL, pattern, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, pattern); + #endif // USE_XFTFONTMATCH_PATCH + + if (xloadsparefont(pattern, FRC_NORMAL)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadsparefont(pattern, FRC_ITALIC)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadsparefont(pattern, FRC_ITALICBOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadsparefont(pattern, FRC_BOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDestroy(pattern); + } +} \ No newline at end of file diff --git a/patch/font2.h b/patch/font2.h new file mode 100644 index 0000000..c0f9ff5 --- /dev/null +++ b/patch/font2.h @@ -0,0 +1,2 @@ +static int xloadsparefont(FcPattern *, int); +static void xloadsparefonts(void); \ No newline at end of file diff --git a/patch/fullscreen_x.c b/patch/fullscreen_x.c new file mode 100644 index 0000000..3fa65f7 --- /dev/null +++ b/patch/fullscreen_x.c @@ -0,0 +1,17 @@ +void +fullscreen(const Arg *arg) +{ + XEvent ev; + + memset(&ev, 0, sizeof(ev)); + + ev.xclient.type = ClientMessage; + ev.xclient.message_type = xw.netwmstate; + ev.xclient.display = xw.dpy; + ev.xclient.window = xw.win; + ev.xclient.format = 32; + ev.xclient.data.l[0] = 2; /* _NET_WM_STATE_TOGGLE */ + ev.xclient.data.l[1] = xw.netwmfullscreen; + + XSendEvent(xw.dpy, DefaultRootWindow(xw.dpy), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); +} diff --git a/patch/fullscreen_x.h b/patch/fullscreen_x.h new file mode 100644 index 0000000..28a833b --- /dev/null +++ b/patch/fullscreen_x.h @@ -0,0 +1 @@ +static void fullscreen(const Arg *arg); diff --git a/patch/invert.c b/patch/invert.c new file mode 100644 index 0000000..2260b5d --- /dev/null +++ b/patch/invert.c @@ -0,0 +1,21 @@ +static int invertcolors = 0; + +void +invert(const Arg *dummy) +{ + invertcolors = !invertcolors; + redraw(); +} + +Color +invertedcolor(Color *clr) +{ + XRenderColor rc; + Color inverted; + rc.red = ~clr->color.red; + rc.green = ~clr->color.green; + rc.blue = ~clr->color.blue; + rc.alpha = clr->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &rc, &inverted); + return inverted; +} \ No newline at end of file diff --git a/patch/invert.h b/patch/invert.h new file mode 100644 index 0000000..0b0931f --- /dev/null +++ b/patch/invert.h @@ -0,0 +1 @@ +static void invert(const Arg *); \ No newline at end of file diff --git a/patch/iso14755.c b/patch/iso14755.c new file mode 100644 index 0000000..a2fbd79 --- /dev/null +++ b/patch/iso14755.c @@ -0,0 +1,21 @@ +void +iso14755(const Arg *arg) +{ + FILE *p; + char *us, *e, codepoint[9], uc[UTF_SIZ]; + unsigned long utf32; + + if (!(p = popen(ISO14755CMD, "r"))) + return; + + us = fgets(codepoint, sizeof(codepoint), p); + pclose(p); + + if (!us || *us == '\0' || *us == '-' || strlen(us) > 7) + return; + if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || + (*e != '\n' && *e != '\0')) + return; + + ttywrite(uc, utf8encode(utf32, uc), 1); +} \ No newline at end of file diff --git a/patch/iso14755.h b/patch/iso14755.h new file mode 100644 index 0000000..45a3d7a --- /dev/null +++ b/patch/iso14755.h @@ -0,0 +1,6 @@ +#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) + +/* constants */ +#define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: mode = ATTR_REVERSE, + deb->u = *z, + deb->fg = defaultfg, deb->bg = defaultbg; + } else if (type < 5) + memcpy(term.line[bot], g, col * sizeof(Glyph)); + else { + for (deb = &term.line[bot][0], fin = &term.line[bot][col]; deb < fin; deb++) + deb->mode = ATTR_REVERSE, + deb->u = ' ', + deb->fg = defaultfg, deb->bg = defaultbg; + term.line[bot][0].u = ksym; + } + + term.dirty[bot] = 1; + drawregion(0, bot, col, bot + 1); +} + +#if SCROLLBACK_PATCH && KEYBOARDSELECT_PATCH +Glyph getglyph(Term term, int y, int x) +{ + Glyph g; + int realy = y - term.scr; + if(realy >= 0) { + g = term.line[realy][x]; + } else { + realy = term.histi - term.scr + y + 1; + g = term.hist[realy][x]; + } + return g; +} +#endif + +void select_or_drawcursor(int selectsearch_mode, int type) +{ + int done = 0; + + if (selectsearch_mode & 1) { + selextend(term.c.x, term.c.y, type, done); + xsetsel(getsel()); + } else { + #if LIGATURES_PATCH + xdrawcursor(term.c.x, term.c.y, term.line[term.c.y][term.c.x], + term.ocx, term.ocy, term.line[term.ocy][term.ocx], + term.line[term.ocy], term.col); + #elif SCROLLBACK_PATCH && KEYBOARDSELECT_PATCH + xdrawcursor(term.c.x, term.c.y, getglyph(term, term.c.y, term.c.x), + term.ocx, term.ocy, getglyph(term, term.ocy, term.ocx)); + #else + xdrawcursor(term.c.x, term.c.y, term.line[term.c.y][term.c.x], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + #endif // LIGATURES_PATCH + } +} + +void search(int selectsearch_mode, Rune *target, int ptarget, int incr, int type, TCursor *cu) +{ + Rune *r; + int i, bound = (term.col * cu->y + cu->x) * (incr > 0) + incr; + + for (i = term.col * term.c.y + term.c.x + incr; i != bound; i += incr) { + for (r = target; r - target < ptarget; r++) { + if (*r == term.line[(i + r - target) / term.col][(i + r - target) % term.col].u) { + if (r - target == ptarget - 1) + break; + } else { + r = NULL; + break; + } + } + if (r != NULL) + break; + } + + if (i != bound) { + term.c.y = i / term.col, term.c.x = i % term.col; + select_or_drawcursor(selectsearch_mode, type); + } +} + +int trt_kbdselect(KeySym ksym, char *buf, int len) +{ + static TCursor cu; + static Rune target[64]; + static int type = 1, ptarget, in_use; + static int sens, quant; + static char selectsearch_mode; + int i, bound, *xy; + + if (selectsearch_mode & 2) { + if (ksym == XK_Return) { + selectsearch_mode ^= 2; + set_notifmode(selectsearch_mode, -2); + if (ksym == XK_Escape) + ptarget = 0; + return 0; + } else if (ksym == XK_BackSpace) { + if (!ptarget) + return 0; + term.line[term.bot][ptarget--].u = ' '; + } else if (len < 1) { + return 0; + } else if (ptarget == term.col || ksym == XK_Escape) { + return 0; + } else { + utf8decode(buf, &target[ptarget++], len); + term.line[term.bot][ptarget].u = target[ptarget - 1]; + } + + if (ksym != XK_BackSpace) + search(selectsearch_mode, &target[0], ptarget, sens, type, &cu); + + term.dirty[term.bot] = 1; + drawregion(0, term.bot, term.col, term.bot + 1); + return 0; + } + + switch (ksym) { + case -1: + in_use = 1; + cu.x = term.c.x, cu.y = term.c.y; + set_notifmode(0, ksym); + return MODE_KBDSELECT; + case XK_s: + if (selectsearch_mode & 1) + selclear(); + else + selstart(term.c.x, term.c.y, 0); + set_notifmode(selectsearch_mode ^= 1, ksym); + break; + case XK_t: + selextend(term.c.x, term.c.y, type ^= 3, i = 0); /* 2 fois */ + selextend(term.c.x, term.c.y, type, i = 0); + break; + case XK_slash: + case XK_KP_Divide: + case XK_question: + ksym &= XK_question; /* Divide to slash */ + sens = (ksym == XK_slash) ? -1 : 1; + ptarget = 0; + set_notifmode(15, ksym); + selectsearch_mode ^= 2; + break; + case XK_Escape: + if (!in_use) + break; + selclear(); + case XK_Return: + set_notifmode(4, ksym); + term.c.x = cu.x, term.c.y = cu.y; + select_or_drawcursor(selectsearch_mode = 0, type); + in_use = quant = 0; + return MODE_KBDSELECT; + case XK_n: + case XK_N: + if (ptarget) + search(selectsearch_mode, &target[0], ptarget, (ksym == XK_n) ? -1 : 1, type, &cu); + break; + case XK_BackSpace: + term.c.x = 0; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_dollar: + term.c.x = term.col - 1; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_Home: + term.c.x = 0, term.c.y = 0; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_End: + term.c.x = cu.x, term.c.y = cu.y; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_Page_Up: + case XK_Page_Down: + term.c.y = (ksym == XK_Prior ) ? 0 : cu.y; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_exclam: + term.c.x = term.col >> 1; + select_or_drawcursor(selectsearch_mode, type); + break; + case XK_asterisk: + case XK_KP_Multiply: + term.c.x = term.col >> 1; + case XK_underscore: + term.c.y = cu.y >> 1; + select_or_drawcursor(selectsearch_mode, type); + break; + default: + if (ksym >= XK_0 && ksym <= XK_9) { /* 0-9 keyboard */ + quant = (quant * 10) + (ksym ^ XK_0); + return 0; + } else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) { /* 0-9 numpad */ + quant = (quant * 10) + (ksym ^ XK_KP_0); + return 0; + } else if (ksym == XK_k || ksym == XK_h) + i = ksym & 1; + else if (ksym == XK_l || ksym == XK_j) + i = ((ksym & 6) | 4) >> 1; + else if ((XK_Home & ksym) != XK_Home || (i = (ksym ^ XK_Home) - 1) > 3) + break; + + xy = (i & 1) ? &term.c.y : &term.c.x; + sens = (i & 2) ? 1 : -1; + bound = (i >> 1 ^ 1) ? 0 : (i ^ 3) ? term.col - 1 : term.bot; + + if (quant == 0) + quant++; + + if (*xy == bound && ((sens < 0 && bound == 0) || (sens > 0 && bound > 0))) + break; + + *xy += quant * sens; + if (*xy < 0 || ( bound > 0 && *xy > bound)) + *xy = bound; + + select_or_drawcursor(selectsearch_mode, type); + } + quant = 0; + return 0; +} diff --git a/patch/keyboardselect_st.h b/patch/keyboardselect_st.h new file mode 100644 index 0000000..3fa1ad7 --- /dev/null +++ b/patch/keyboardselect_st.h @@ -0,0 +1,2 @@ +void toggle_winmode(int); +int trt_kbdselect(KeySym, char *, int); \ No newline at end of file diff --git a/patch/keyboardselect_x.c b/patch/keyboardselect_x.c new file mode 100644 index 0000000..16af3e1 --- /dev/null +++ b/patch/keyboardselect_x.c @@ -0,0 +1,7 @@ +void toggle_winmode(int flag) { + win.mode ^= flag; +} + +void keyboard_select(const Arg *dummy) { + win.mode ^= trt_kbdselect(-1, NULL, 0); +} \ No newline at end of file diff --git a/patch/keyboardselect_x.h b/patch/keyboardselect_x.h new file mode 100644 index 0000000..6b1241a --- /dev/null +++ b/patch/keyboardselect_x.h @@ -0,0 +1,2 @@ +void toggle_winmode(int); +void keyboard_select(const Arg *); \ No newline at end of file diff --git a/patch/netwmicon.h b/patch/netwmicon.h new file mode 100644 index 0000000..5e9c654 --- /dev/null +++ b/patch/netwmicon.h @@ -0,0 +1,686 @@ +unsigned long icon[] = { + 64, 64, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, + 0x03000000, 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x03000000, + 0x20181818, 0x4e868686, 0x74b2b2b2, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, + 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x77b6b6b6, 0x74b2b2b2, + 0x4e868686, 0x20181818, 0x03000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x03000000, 0x46717171, 0xcef3f3f3, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xcdf3f3f3, + 0x456f6f6f, 0x03000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x211f1f1f, 0xd1f4f4f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd0f3f3f3, 0x20181818, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x59959595, 0xffffffff, + 0xffffffff, 0xff8b8b8b, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff8c8c8c, 0xffffffff, 0xffffffff, 0x58919191, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x83b3b3b3, 0xffffffff, 0xffffffff, 0xff262626, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff262626, 0xffffffff, + 0xffffffff, 0x83b3b3b3, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff2c2c2c, 0xffe0e0e0, 0xff1c1c1c, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff202020, + 0xff6c6c6c, 0xffffffff, 0xff6d6d6d, 0xff3c3c3c, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff2d2d2d, 0xffe1e1e1, 0xffc3c3c3, 0xffffffff, + 0xffa1a1a1, 0xffdddddd, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff7f7f7f, 0xffbfbfbf, 0xff303030, 0xffffffff, 0xff1c1c1c, 0xff181818, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff717171, 0xffe1e1e1, + 0xff545454, 0xffffffff, 0xff1c1c1c, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff1c1c1c, 0xffa1a1a1, 0xfff6f6f6, 0xffffffff, + 0xffaeaeae, 0xff515151, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff343434, 0xffffffff, 0xff979797, 0xfff9f9f9, + 0xff515151, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff303030, 0xffffffff, 0xff1c1c1c, 0xffb3b3b3, 0xff8d8d8d, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff686868, 0xff616161, 0xff3c3c3c, 0xffffffff, + 0xff545454, 0xffe8e8e8, 0xff5b5b5b, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff4b4b4b, 0xffb3b3b3, 0xffe1e1e1, 0xffffffff, 0xffcccccc, 0xff717171, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff303030, 0xffffffff, 0xff1c1c1c, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff2b2b2b, 0xffd1d1d1, + 0xff1c1c1c, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff7f7f7f, 0xffe9e9e9, 0xffe9e9e9, 0xffe9e9e9, 0xffe9e9e9, + 0xffe9e9e9, 0xffe9e9e9, 0xff444444, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff222222, 0xffffffff, + 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8eacacac, 0xffffffff, 0xffffffff, 0xff222222, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8eacacac, 0xffffffff, + 0xffffffff, 0xff222222, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff222222, 0xffffffff, 0xffffffff, 0x8eacacac, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x8aacacac, 0xffffffff, 0xffffffff, 0xff262626, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff171717, + 0xff171717, 0xff171717, 0xff171717, 0xff171717, 0xff262626, 0xffffffff, + 0xffffffff, 0x8aacacac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x62858585, 0xffffffff, 0xffffffff, 0xff8c8c8c, 0xff373737, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, 0xff363636, + 0xff363636, 0xff373737, 0xff8d8d8d, 0xffffffff, 0xffffffff, 0x62828282, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x21171717, 0xdee2e2e2, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xdee1e1e1, 0x1f101010, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x03000000, 0x5f4e4e4e, 0xdbe1e1e1, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xdae1e1e1, + 0x5f4b4b4b, 0x03000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x12000000, 0x48040404, 0x6d595959, 0x8d929292, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, 0x90979797, + 0x90979797, 0x8d929292, 0x6d595959, 0x48040404, 0x12000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x06000000, 0x22000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, 0x29000000, + 0x22000000, 0x06000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; \ No newline at end of file diff --git a/patch/newterm.c b/patch/newterm.c new file mode 100644 index 0000000..d4a1735 --- /dev/null +++ b/patch/newterm.c @@ -0,0 +1,30 @@ +void +newterm(const Arg* a) +{ + int res; + switch (fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + switch (fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + res = chdir(getcwd_by_pid(pid)); + execlp("st", "./st", NULL); + break; + default: + exit(0); + } + default: + wait(NULL); + } +} + +static char *getcwd_by_pid(pid_t pid) { + char buf[32]; + snprintf(buf, sizeof buf, "/proc/%d/cwd", pid); + return realpath(buf, NULL); +} \ No newline at end of file diff --git a/patch/newterm.h b/patch/newterm.h new file mode 100644 index 0000000..ea7292c --- /dev/null +++ b/patch/newterm.h @@ -0,0 +1,2 @@ +void newterm(const Arg *); +static char *getcwd_by_pid(pid_t pid); \ No newline at end of file diff --git a/patch/normalMode.c b/patch/normalMode.c new file mode 100644 index 0000000..bbc9cfb --- /dev/null +++ b/patch/normalMode.c @@ -0,0 +1,284 @@ +#include +#include + +#include "normalMode.h" +#include "utils.h" + +extern Glyph const styleSearch, style[]; +extern char const wDelS[], wDelL[], *nmKeys[]; +extern unsigned int bg[], fg, currentBg, highlightBg, highlightFg, amountNmKeys; + +typedef struct { int p[3]; } Pos; + +typedef enum {visual='v', visualLine='V', yank = 'y'} Op; +typedef enum {infix_none=0, infix_i='i', infix_a='a'} Infix; +typedef enum {fw='/', bw='?'} Search; +struct NormalModeState { + struct OperationState { Op op; Infix infix; } cmd; + struct MotionState { uint32_t c; int active; Pos searchPos; Search search; } m; +} defaultNormalMode, state; + +DynamicArray searchStr=UTF8_ARRAY, cCmd=UTF8_ARRAY, lCmd=UTF8_ARRAY; +Glyph styleCmd; +char posBuffer[10], braces[6][3] = { {"()"}, {"<>"}, {"{}"}, {"[]"}, {"\"\""}, {"''"}}; +int exited=1, overlay=1; +static inline Rune cChar() { return term.line[term.c.y][term.c.x].u; } +static inline int pos(int p, int h) {return IS_SET(MODE_ALTSCREEN)?p:rangeY(p+h*histOff-insertOff);} +static inline int contains(Rune l, char const * values, size_t const memSize) { + for (uint32_t i = 0; i < memSize; ++i) if (l == values[i]) return 1; + return 0; +} +static inline void decodeTo(char const *cs, size_t len, DynamicArray *arr) { + char *var = expand(arr); + if (!var) empty(arr); else utf8decode(cs, (Rune*)(var), len); +} +static inline void applyPos(Pos p) { + term.c.x = p.p[0], term.c.y = p.p[1]; + if (!IS_SET(MODE_ALTSCREEN) && histOp) term.line = &buf[histOff = p.p[2]]; +} +/// Find string in history buffer, and provide string-match-lookup for highlighting matches +static int highlighted(int x, int y) { + int const s=term.row*term.col, i=y*term.col+x, sz=size(&searchStr); + return sz && i0)==cChar())?wIdx+1:0; + if (wi && !wIdx) historyMove(-(int)(s*wi), 0, 0); + } + if (wIdx == strSz && wIdx) historyMove(-(int)(s*strSz), 0, 0); + else applyPos(p); + markSearchMatches(all); + return wIdx == strSz; +} +/// Execute series of normal-mode commands from char array / decoded from dynamic array +ExitState pressKeys(char const* s, size_t e) { + ExitState x=success; + for (size_t i=0; iu = *((Rune*)(st->content + (size(st)+x-xEnd)*st->elSize)); +} +/// Expand "infix" expression: for instance (w =>) l b | | v e | | y +static ExitState expandExpression(char l) { // ({ =>) l ? { \n | l | v / } \n | h | y + int a=state.cmd.infix==infix_a, yank=state.cmd.op=='y', lc=tolower(l), found=1; + state.cmd.infix = infix_none; + if(!yank && state.cmd.op!=visual && state.cmd.op!=visualLine) return failed; + char mot[11] = {'l', 0, 'b', 0, 0, 'v', 0, 'e', 0, 0, (char)(yank ? 'y' : 0)}; + if (lc == 'w') mot[2] = (char) ('b' - lc + l), mot[7] = (char) ((a ? 'w' : 'e') - lc + l), mot[9]=(char)(a?'h':0); + else { + mot[1]='?', mot[3]=mot[8]='\n', mot[6]='/', mot[4]=(char)(a?0:'l'), mot[9]=(char)(a?0:'h'); + for (int i=found=0; !found && i < 6; ++i) + if ((found=contains(l,braces[i],2))) mot[2]=braces[i][0], mot[7]=braces[i][1]; + } + if (!found) return failed; + assign(&lCmd, &cCmd); + empty(&cCmd); + state.cmd = defaultNormalMode.cmd; + return pressKeys(mot, 11); +} + +ExitState executeMotion(char const cs, KeySym const *const ks) { + state.m.c = state.m.c < 1u ? 1u : state.m.c; + if (ks && *ks == XK_d) historyMove(0, 0, term.row / 2); + else if (ks && *ks == XK_u) historyMove(0, 0, -term.row / 2); + else if (ks && *ks == XK_f) historyMove(0, 0, term.row-1+(term.c.y=0)); + else if (ks && *ks == XK_b) historyMove(0, 0, -(term.c.y=term.row-1)); + else if (ks && *ks == XK_h) overlay = !overlay; + else if (cs == 'K') historyMove(0, 0, -(int)state.m.c); + else if (cs == 'J') historyMove(0, 0, (int)state.m.c); + else if (cs == 'k') historyMove(0, -(int)state.m.c, 0); + else if (cs == 'j') historyMove(0, (int)state.m.c, 0); + else if (cs == 'h') historyMove(-(int)state.m.c, 0, 0); + else if (cs == 'l') historyMove( (int)state.m.c, 0, 0); + else if (cs == 'H') term.c.y = 0; + else if (cs == 'M') term.c.y = term.bot / 2; + else if (cs == 'L') term.c.y = term.bot; + else if (cs == 's' || cs == 'S') altToggle = cs == 's' ? !altToggle : 1; + else if (cs == 'G' || cs == 'g') { + if (cs == 'G') term.c = c[0] = c[IS_SET(MODE_ALTSCREEN)+1]; + if (!IS_SET(MODE_ALTSCREEN)) term.line = &buf[histOff=insertOff]; + } else if (cs == '0') term.c.x = 0; + else if (cs == '$') term.c.x = term.col-1; + else if (cs == 't') sel.type = sel.type==SEL_REGULAR ? SEL_RECTANGULAR : SEL_REGULAR; + else if (cs == 'n' || cs == 'N') { + int const d = ((cs=='N')!=(state.m.search==bw))?-1:1; + for (uint32_t i = state.m.c; i && findString(d, 0); --i); + } else if (contains(cs, "wWeEbB", 6)) { + int const low=cs<=90, off=tolower(cs)!='w', sgn=(tolower(cs)=='b')?-1:1; + size_t const l=strlen(wDelL), s=strlen(wDelS), maxIt=rows()*term.col; + for (int it=0, on=0; state.m.c > 0 && it < maxIt; ++it) { + // If an offset is to be performed in beginning or not in beginning, move in history. + if ((off || it) && historyMove(sgn, 0, 0)) break; + // Determine if the category of the current letter changed since last iteration. + int n = 1<<(contains(cChar(),wDelS,s) ?(2-low) :!contains(cChar(),wDelL,l)), + found = (on|=n)^n && ((off ?on^n :n)!=1); + // If a reverse offset is to be performed and this is the last letter: + if (found && off) historyMove(-sgn, 0, 0); + // Terminate iteration: reset #it and old n value #on and decrease operation count: + if (found) it=-1, on=0, --state.m.c; + } + } else return failed; + state.m.c = 0; + return state.cmd.op == yank ? exitMotion : success; +} + +ExitState kPressHist(char const *cs, size_t len, int ctrl, KeySym const *kSym) { + historyOpToggle(1, 1); + int const prevYOff=IS_SET(MODE_ALTSCREEN)?0:histOff, search=state.m.search&&state.m.active, + prevAltToggle=altToggle, prevOverlay=overlay; + int const noOp=!state.cmd.op&&!state.cmd.infix, num=len==1&&BETWEEN(cs[0],48,57), + esc=kSym&&*kSym==XK_Escape, ret=(kSym&&*kSym==XK_Return)||(len==1&&cs[0]=='\n'), + quantifier=num&&(cs[0]!='0'||state.m.c), ins=!search &&noOp &&len &&cs[0]=='i'; + exited = 0; + ExitState result = success; + if (esc || ret || ins) { result = exitMotion, len = 0; + } else if (kSym && *kSym == XK_BackSpace) { + if ((search || state.m.c) && size(&cCmd)) pop(&cCmd); + if (search) { + if (size(&searchStr)) pop(&searchStr); + else result = exitMotion; + if (!size(&searchStr)) tfulldirt(); + applyPos(state.m.searchPos); + findString(state.m.search==fw ? 1 : -1, 1); + } else if (state.m.c) state.m.c /= 10; + len = 0; + } else if (search) { + if (len >= 1) decodeTo(cs, len, &searchStr); + applyPos(state.m.searchPos); + findString(state.m.search==fw ? 1 : -1, 1); + } else if (len == 0) { result = failed; + } else if (quantifier) { state.m.c = min(SHRT_MAX, (int)state.m.c*10+cs[0]-48); + } else if (state.cmd.infix && state.cmd.op && (result = expandExpression(cs[0]), len=0)) { + } else if (cs[0] == 'd') { state = defaultNormalMode; result = exitMotion; state.m.active = 1; + } else if (cs[0] == '.') { + if (size(&cCmd)) assign(&lCmd, &cCmd); + empty(&cCmd); + executeCommand((uint32_t*) lCmd.content, size(&lCmd)); + empty(&cCmd); + len = 0; + } else if (cs[0] == 'r') { tfulldirt(); + } else if (cs[0] == 'c') { + empty(&lCmd); + empty(&cCmd); + empty(&searchStr); + tfulldirt(); + len = 0; + } else if (cs[0] == fw || cs[0] == bw) { + empty(&searchStr); + state.m.search = (Search) cs[0]; + state.m.searchPos = (Pos){.p={term.c.x, term.c.y, prevYOff}}; + state.m.active = 1; + } else if (cs[0]==infix_i || cs[0]==infix_a) { state.cmd.infix=(Infix) cs[0]; + } else if (cs[0] == 'y') { + if (state.cmd.op) { + result = (state.cmd.op == yank || state.cmd.op == visualLine) ? exitOp : exitMotion; + if (state.cmd.op == yank) selstart(0, term.c.y, 0); + } else selstart(term.c.x, term.c.y, 0); + state.cmd.op = yank; + } else if (cs[0] == visual || cs[0] == visualLine) { + if (state.cmd.op != (Op) cs[0]) { + state.cmd = defaultNormalMode.cmd; + state.cmd.op = (Op) cs[0]; + selstart(cs[0] == visualLine ?0 :term.c.x, term.c.y, 0); + } else result = exitOp; + } else if (!(result =executeMotion((char) (len?cs[0]:0), ctrl?kSym:NULL))) { + result=failed; + for (size_t i = 0; !ctrl && i < amountNmKeys; ++i) + if (cs[0]==nmKeys[i][0] && + failed!=(result=pressKeys(&nmKeys[i][1], strlen(nmKeys[i])-1))) goto end; + } // Operation/Motion finished if valid: update cmd string, extend selection, update search + if (result != failed) { + if (len == 1 && !ctrl) decodeTo(cs, len, &cCmd); + if ((state.cmd.op == visualLine) || ((state.cmd.op == yank) && (result == exitOp))) { + int const off = term.c.y + (IS_SET(MODE_ALTSCREEN) ? 0 : histOff) < sel.ob.y; //< Selection start below end. + sel.ob.x = off ? term.col - 1 : 0; + selextend(off ? 0 : term.col-1, term.c.y, sel.type, 0); + } else if (sel.oe.x != -1) { + selextend(term.c.x, term.c.y, sel.type, 0); + } + } // Set repaint for motion or status bar + if (!IS_SET(MODE_ALTSCREEN) && prevYOff != histOff) tfulldirt(); + // Terminate Motion / operation if thus indicated + if (result == exitMotion) { + if (!state.m.active) result = (exited=noOp) ? finish : exitOp; + state.m.active = (int) (state.m.c = 0u); + } + if (result == exitOp || result == finish) { + if (state.cmd.op == yank) { + xsetsel(getsel()); + xclipcopy(); + } + state = defaultNormalMode; + selclear(); + if (!esc) assign(&lCmd, &cCmd); + empty(&cCmd); + } // Update the content displayed in the history overlay + styleCmd = style[state.cmd.op==yank ? 1 : (state.cmd.op==visual ? 2 : + (state.cmd.op==visualLine ? 3 :0))]; + int const posLin = !IS_SET(MODE_ALTSCREEN) ? rangeY(insertOff-histOff):0, h=rows()-term.row; + if (!posLin || posLin==h || !h) strcpy(posBuffer, posLin ? " [BOT] " : " [TOP] "); + else sprintf(posBuffer, " % 3d%c ", min(100, max(0, (int)(.5 + posLin * 100. / h))),'%'); + if ((overlay || overlay!=prevOverlay) && term.col>9 && term.row>4) { + if (!term.dirty[term.row-1]) xdrawline(term.line[term.row-1], term.col*2/3, term.row-1, term.col-1); + if (!term.dirty[term.row-2]) xdrawline(term.line[term.row-2], term.col*2/3, term.row-2, term.col-1); + } + if (result==finish) altToggle = 0; + if (altToggle != prevAltToggle) tswapscreen(); +end: + historyOpToggle(-1, 1); + return result; +} + +void historyOverlay(int x, int y, Glyph* g) { + if (!histMode) return; + TCursor const *cHist = histOp ? &term.c : &c[0]; + if(overlay && term.col > 9 && term.row > 4 && (x > (2*term.col/3)) && (y >= (term.row-2))) { + *g = (y == term.row - 2) ? styleSearch : styleCmd; + if (y == term.row-2) getChar(&searchStr, g, term.row-2, term.col-2, term.col/3, x); + else if (x > term.col - 7) g->u = (Rune)(posBuffer[x - term.col + 7]); + else getChar(size(&cCmd) ?&cCmd :&lCmd, g, term.row-1, term.col-7, term.col/3-6, x); + } else if (highlighted(x, y)) g->bg = highlightBg, g->fg = highlightFg; + else if ((x==cHist->x) ^ (y==cHist->y)) g->bg = currentBg; + else if (x==cHist->x) g->mode^=ATTR_REVERSE; +} +void historyPreDraw() { + static Pos op = {.p={0, 0, 0}}; + historyOpToggle(1, 0); + // Draw the cursor cross if changed + if (term.c.y >= term.row || op.p[1] >= term.row) tfulldirt(); + else if (exited || (op.p[1] != term.c.y)) term.dirty[term.c.y] = term.dirty[op.p[1]] = 1; + for (int i=0; (exited || term.c.x != op.p[0]) && iv, clip); + res = system(cmd); +} \ No newline at end of file diff --git a/patch/opencopied.h b/patch/opencopied.h new file mode 100644 index 0000000..3734ac7 --- /dev/null +++ b/patch/opencopied.h @@ -0,0 +1 @@ +void opencopied(const Arg *); \ No newline at end of file diff --git a/patch/openurlonclick.c b/patch/openurlonclick.c new file mode 100644 index 0000000..626cde6 --- /dev/null +++ b/patch/openurlonclick.c @@ -0,0 +1,141 @@ +#if SCROLLBACK_PATCH && !VIM_BROWSE_PATCH +#define TLINEURL(y) TLINE(y) +#else +#define TLINEURL(y) term.line[y] +#endif // SCROLLBACK_PATCH + +#if VIM_BROWSE_PATCH +extern int buffCols; +#endif // VIM_BROWSE_PATCH + +int url_x1, url_y1, url_x2, url_y2 = -1; +int url_draw, url_click, url_maxcol; + +static int +isvalidurlchar(Rune u) +{ + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. See copyurl patch. + */ + static char urlchars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + return u < 128 && strchr(urlchars, (int)u) != NULL; +} + +/* find the end of the wrapped line */ +static int +findeowl(int row) +{ + #if VIM_BROWSE_PATCH + int col = buffCols - 1; + #elif COLUMNS_PATCH + int col = term.maxcol - 1; + #else + int col = term.col - 1; + #endif // VIM_BROWSE_PATCH + + do { + if (TLINEURL(row)[col].mode & ATTR_WRAP) + return col; + } while (TLINEURL(row)[col].u == ' ' && --col >= 0); + return -1; +} + +void +clearurl(void) +{ + while (url_y1 <= url_y2 && url_y1 < term.row) + term.dirty[url_y1++] = 1; + url_y2 = -1; +} + +char * +detecturl(int col, int row, int draw) +{ + static char url[2048]; + int x1, y1, x2, y2, wrapped; + int row_start = row; + int col_start = col; + int i = sizeof(url)/2+1, j = sizeof(url)/2; + #if SCROLLBACK_PATCH && !VIM_BROWSE_PATCH + int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1; + /* Fixme: MODE_ALTSCREEN is not defined here, I had to use the magic number 1<<2 */ + if ((term.mode & (1 << 2)) != 0) + minrow = 0, maxrow = term.row - 1; + #else + int minrow = 0, maxrow = term.row - 1; + #endif // scrollback_patch + url_maxcol = 0; + + /* clear previously underlined url */ + if (draw) + clearurl(); + + if (!isvalidurlchar(TLINEURL(row)[col].u)) + return NULL; + + /* find the first character of url */ + do { + x1 = col_start, y1 = row_start; + url_maxcol = MAX(url_maxcol, x1); + url[--i] = TLINEURL(row_start)[col_start].u; + if (--col_start < 0) { + if (--row_start < minrow || (col_start = findeowl(row_start)) < 0) + break; + } + } while (i > 0 && isvalidurlchar(TLINEURL(row_start)[col_start].u)); + + /* early detection */ + if (url[i] != 'h') + return NULL; + + /* find the last character of url */ + do { + x2 = col, y2 = row; + url_maxcol = MAX(url_maxcol, x2); + url[j++] = TLINEURL(row)[col].u; + wrapped = TLINEURL(row)[col].mode & ATTR_WRAP; + #if VIM_BROWSE_PATCH + if (++col >= buffCols || wrapped) { + #elif COLUMNS_PATCH + if (++col >= term.maxcol || wrapped) { + #else + if (++col >= term.col || wrapped) { + #endif // VIM_BROWSE_PATCH + col = 0; + if (++row > maxrow || !wrapped) + break; + } + } while (j < sizeof(url)-1 && isvalidurlchar(TLINEURL(row)[col].u)); + + url[j] = 0; + + if (strncmp("https://", &url[i], 8) && strncmp("http://", &url[i], 7)) + return NULL; + + /* underline url (see xdrawglyphfontspecs() in x.c) */ + if (draw) { + url_x1 = (y1 >= 0) ? x1 : 0; + url_x2 = (y2 < term.row) ? x2 : url_maxcol; + url_y1 = MAX(y1, 0); + url_y2 = MIN(y2, term.row-1); + url_draw = 1; + for (y1 = url_y1; y1 <= url_y2; y1++) + term.dirty[y1] = 1; + } + + return &url[i]; +} + +void +openUrlOnClick(int col, int row, char* url_opener) +{ + char *url = detecturl(col, row, 1); + if (url) { + extern char **environ; + pid_t junk; + char *argv[] = { url_opener, url, NULL }; + posix_spawnp(&junk, argv[0], NULL, NULL, argv, environ); + } +} diff --git a/patch/openurlonclick.h b/patch/openurlonclick.h new file mode 100644 index 0000000..5aa09de --- /dev/null +++ b/patch/openurlonclick.h @@ -0,0 +1,8 @@ +#include + +static inline void restoremousecursor(void) { + if (!(win.mode & MODE_MOUSE) && xw.pointerisvisible) + XDefineCursor(xw.dpy, xw.win, xw.vpointer); +} +static void clearurl(void); +static void openUrlOnClick(int col, int row, char* url_opener); diff --git a/patch/rightclicktoplumb_st.c b/patch/rightclicktoplumb_st.c new file mode 100644 index 0000000..c60b946 --- /dev/null +++ b/patch/rightclicktoplumb_st.c @@ -0,0 +1,19 @@ +#if defined(__OpenBSD__) + #include +#endif + +int +subprocwd(char *path) +{ +#if defined(__linux) + if (snprintf(path, PATH_MAX, "/proc/%d/cwd", pid) < 0) + return -1; + return 0; +#elif defined(__OpenBSD__) + size_t sz = PATH_MAX; + int name[3] = {CTL_KERN, KERN_PROC_CWD, pid}; + if (sysctl(name, 3, path, &sz, 0, 0) == -1) + return -1; + return 0; +#endif +} \ No newline at end of file diff --git a/patch/rightclicktoplumb_st.h b/patch/rightclicktoplumb_st.h new file mode 100644 index 0000000..98e42ab --- /dev/null +++ b/patch/rightclicktoplumb_st.h @@ -0,0 +1 @@ +int subprocwd(char *); \ No newline at end of file diff --git a/patch/rightclicktoplumb_x.c b/patch/rightclicktoplumb_x.c new file mode 100644 index 0000000..1b9b2bc --- /dev/null +++ b/patch/rightclicktoplumb_x.c @@ -0,0 +1,24 @@ +#include + +void +plumb(char *sel) { + if (sel == NULL) + return; + char cwd[PATH_MAX]; + pid_t child; + if (subprocwd(cwd) != 0) + return; + + switch(child = fork()) { + case -1: + return; + case 0: + if (chdir(cwd) != 0) + exit(1); + if (execvp(plumb_cmd, (char *const []){plumb_cmd, sel, 0}) == -1) + exit(1); + exit(0); + default: + waitpid(child, NULL, 0); + } +} \ No newline at end of file diff --git a/patch/rightclicktoplumb_x.h b/patch/rightclicktoplumb_x.h new file mode 100644 index 0000000..d0c8b88 --- /dev/null +++ b/patch/rightclicktoplumb_x.h @@ -0,0 +1 @@ +void plumb(char *); \ No newline at end of file diff --git a/patch/scrollback.c b/patch/scrollback.c new file mode 100644 index 0000000..2644cfe --- /dev/null +++ b/patch/scrollback.c @@ -0,0 +1,55 @@ +void +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } + + #if SIXEL_PATCH + scroll_images(-1*n); + #endif // SIXEL_PATCH + + #if OPENURLONCLICK_PATCH + if (n > 0) + restoremousecursor(); + #endif // OPENURLONCLICK_PATCH +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + if (n < 0) + n = term.row + n; + + if (term.scr + n > term.histn) + n = term.histn - term.scr; + + if (!n) + return; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } + + #if SIXEL_PATCH + scroll_images(n); + #endif // SIXEL_PATCH + + #if OPENURLONCLICK_PATCH + if (n > 0) + restoremousecursor(); + #endif // OPENURLONCLICK_PATCH +} diff --git a/patch/scrollback.h b/patch/scrollback.h new file mode 100644 index 0000000..08940f0 --- /dev/null +++ b/patch/scrollback.h @@ -0,0 +1,17 @@ +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); + +#if SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH +typedef struct { + uint b; + uint mask; + void (*func)(const Arg *); + const Arg arg; +} MouseKey; + +extern MouseKey mkeys[]; +#endif // SCROLLBACK_MOUSE_PATCH / SCROLLBACK_MOUSE_ALTSCREEN_PATCH diff --git a/patch/sixel_st.c b/patch/sixel_st.c new file mode 100644 index 0000000..ed64fee --- /dev/null +++ b/patch/sixel_st.c @@ -0,0 +1,42 @@ +sixel_state_t sixel_st; + +void +dcshandle(void) +{ + switch (csiescseq.mode[0]) { + default: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case 'q': /* DECSIXEL */ + if (sixel_parser_init(&sixel_st, 0, 0 << 16 | 0 << 8 | 0, 1, win.cw, win.ch) != 0) + perror("sixel_parser_init() failed"); + term.mode |= MODE_SIXEL; + break; + } +} + +void +scroll_images(int n) { + ImageList *im; + int tmp; + + /* maximum sixel distance in lines from current view before + * deallocation + * TODO: should be in config.h */ + int max_sixel_distance = 10000; + + for (im = term.images; im; im = im->next) { + im->y += n; + + /* check if the current sixel has exceeded the maximum + * draw distance, and should therefore be deleted */ + tmp = im->y; + if (tmp < 0) { tmp = tmp * -1; } + if (tmp > max_sixel_distance) { + fprintf(stderr, "im@0x%08x exceeded maximum distance\n"); + im->should_delete = 1; + } + } +} \ No newline at end of file diff --git a/patch/sixel_st.h b/patch/sixel_st.h new file mode 100644 index 0000000..cf2f0e1 --- /dev/null +++ b/patch/sixel_st.h @@ -0,0 +1,2 @@ +static void dcshandle(void); +static void scroll_images(int n); \ No newline at end of file diff --git a/patch/sixel_x.c b/patch/sixel_x.c new file mode 100644 index 0000000..0f74f53 --- /dev/null +++ b/patch/sixel_x.c @@ -0,0 +1,14 @@ +void +delete_image(ImageList *im) +{ + if (im->prev) + im->prev->next = im->next; + else + term.images = im->next; + if (im->next) + im->next->prev = im->prev; + if (im->pixmap) + XFreePixmap(xw.dpy, (Drawable)im->pixmap); + free(im->pixels); + free(im); +} \ No newline at end of file diff --git a/patch/st_embedder_x.c b/patch/st_embedder_x.c new file mode 100644 index 0000000..c73c389 --- /dev/null +++ b/patch/st_embedder_x.c @@ -0,0 +1,50 @@ +static Window embed; + +void +createnotify(XEvent *e) +{ + XWindowChanges wc; + + if (embed || e->xcreatewindow.override_redirect) + return; + + embed = e->xcreatewindow.window; + + XReparentWindow(xw.dpy, embed, xw.win, 0, 0); + XSelectInput(xw.dpy, embed, PropertyChangeMask | StructureNotifyMask | EnterWindowMask); + + XMapWindow(xw.dpy, embed); + sendxembed(XEMBED_EMBEDDED_NOTIFY, 0, xw.win, 0); + + wc.width = win.w; + wc.height = win.h; + XConfigureWindow(xw.dpy, embed, CWWidth | CWHeight, &wc); + + XSetInputFocus(xw.dpy, embed, RevertToParent, CurrentTime); +} + +void +destroynotify(XEvent *e) +{ + visibility(e); + if (embed == e->xdestroywindow.window) { + focus(e); + } +} + +void +sendxembed(long msg, long detail, long d1, long d2) +{ + XEvent e = { 0 }; + + e.xclient.window = embed; + e.xclient.type = ClientMessage; + e.xclient.message_type = xw.xembed; + e.xclient.format = 32; + e.xclient.data.l[0] = CurrentTime; + e.xclient.data.l[1] = msg; + e.xclient.data.l[2] = detail; + e.xclient.data.l[3] = d1; + e.xclient.data.l[4] = d2; + XSendEvent(xw.dpy, embed, False, NoEventMask, &e); +} diff --git a/patch/st_embedder_x.h b/patch/st_embedder_x.h new file mode 100644 index 0000000..6910fb9 --- /dev/null +++ b/patch/st_embedder_x.h @@ -0,0 +1,7 @@ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_CURRENT 0 + +static void createnotify(XEvent *e); +static void destroynotify(XEvent *e); +static void sendxembed(long msg, long detail, long d1, long d2); \ No newline at end of file diff --git a/patch/st_include.c b/patch/st_include.c new file mode 100644 index 0000000..6772a6e --- /dev/null +++ b/patch/st_include.c @@ -0,0 +1,31 @@ +/* Patches */ +#if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +#include "copyurl.c" +#endif +#if EXTERNALPIPE_PATCH +#include "externalpipe.c" +#endif +#if ISO14755_PATCH +#include "iso14755.c" +#endif +#if KEYBOARDSELECT_PATCH +#include "keyboardselect_st.c" +#endif +#if RIGHTCLICKTOPLUMB_PATCH +#include "rightclicktoplumb_st.c" +#endif +#if NEWTERM_PATCH +#include "newterm.c" +#endif +#if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH +#include "scrollback.c" +#endif +#if SIXEL_PATCH +#include "sixel_st.c" +#endif +#if SYNC_PATCH +#include "sync.c" +#endif +#if VIM_BROWSE_PATCH +#include "normalMode.c" +#endif \ No newline at end of file diff --git a/patch/st_include.h b/patch/st_include.h new file mode 100644 index 0000000..c30a752 --- /dev/null +++ b/patch/st_include.h @@ -0,0 +1,34 @@ +/* Patches */ +#if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +#include "copyurl.h" +#endif +#if EXTERNALPIPE_PATCH +#include "externalpipe.h" +#endif +#if ISO14755_PATCH +#include "iso14755.h" +#endif +#if KEYBOARDSELECT_PATCH +#include "keyboardselect_st.h" +#endif +#if OPENURLONCLICK_PATCH +#include "openurlonclick.h" +#endif +#if RIGHTCLICKTOPLUMB_PATCH +#include "rightclicktoplumb_st.h" +#endif +#if NEWTERM_PATCH +#include "newterm.h" +#endif +#if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH +#include "scrollback.h" +#endif +#if SIXEL_PATCH +#include "sixel_st.h" +#endif +#if SYNC_PATCH +#include "sync.h" +#endif +// #if VIM_BROWSE_PATCH +// #include "normalMode.h" +// #endif diff --git a/patch/sync.c b/patch/sync.c new file mode 100644 index 0000000..a0a815c --- /dev/null +++ b/patch/sync.c @@ -0,0 +1,31 @@ +#include +struct timespec sutv; + +static void +tsync_begin() +{ + clock_gettime(CLOCK_MONOTONIC, &sutv); + su = 1; +} + +static void +tsync_end() +{ + su = 0; +} + +int +tinsync(uint timeout) +{ + struct timespec now; + if (su && !clock_gettime(CLOCK_MONOTONIC, &now) + && TIMEDIFF(now, sutv) >= timeout) + su = 0; + return su; +} + +int +ttyread_pending() +{ + return twrite_aborted; +} \ No newline at end of file diff --git a/patch/sync.h b/patch/sync.h new file mode 100644 index 0000000..6c2ebe7 --- /dev/null +++ b/patch/sync.h @@ -0,0 +1,7 @@ +static int su = 0; +static int twrite_aborted = 0; + +static void tsync_begin(); +static void tsync_end(); +int tinsync(uint timeout); +int ttyread_pending(); \ No newline at end of file diff --git a/patch/utils.h b/patch/utils.h new file mode 100644 index 0000000..5ecea0d --- /dev/null +++ b/patch/utils.h @@ -0,0 +1,23 @@ +/// Dynamic memory-chunk, with (1) datatype size, (2/3) initialized / allocated chunk, (4) content +typedef struct { uint8_t const elSize; uint32_t init, alloc; char* content; } DynamicArray; +#define UTF8_ARRAY {4, 0, 0, NULL} + +static inline int p_alloc(DynamicArray *s, uint32_t amount) { + uint32_t const diff=s->init+s->elSize*amount-s->alloc, nas=s->alloc+max(diff,15)*s->elSize; + if (s->alloc < s->init + s->elSize * amount) { + char* tmp = realloc(s->content, nas); + if (!tmp) return 0; + s->alloc = nas, s->content = tmp; + } + return 1; +} +static inline char *view(DynamicArray * s, uint32_t i) { return s->content + i*s->elSize; } +static inline char *end(DynamicArray *s, uint32_t i) { return s->content +s->init-(i+1)*s->elSize; } +static inline uint32_t getU32(DynamicArray* s, uint32_t i, int b) { return *((uint32_t*) (b ?view(s,i) :end(s,i))); } +static char *expand(DynamicArray *s) { if (!p_alloc(s, 1)) return NULL; s->init += s->elSize; return end(s, 0); } +static inline void pop(DynamicArray* s) { s->init -= s->elSize; } +static inline void empty(DynamicArray* s) { s->init = 0; } +static inline int size(DynamicArray const * s) { return s->init / s->elSize; } +static inline void assign(DynamicArray* s, DynamicArray const *o) { + if (p_alloc(s, size(o))) memcpy(s->content, o->content, (s->init=o->init)); +} \ No newline at end of file diff --git a/patch/x_include.c b/patch/x_include.c new file mode 100644 index 0000000..cbea418 --- /dev/null +++ b/patch/x_include.c @@ -0,0 +1,43 @@ +/* Patches */ +#if ALPHA_PATCH +#include "alpha.c" +#endif +#if BACKGROUND_IMAGE_PATCH +#include "background_image_x.c" +#endif +#if BOXDRAW_PATCH +#include "boxdraw.c" +#endif +#if OPENCOPIED_PATCH +#include "opencopied.c" +#endif +#if FIXKEYBOARDINPUT_PATCH +#include "fixkeyboardinput.c" +#endif +#if FONT2_PATCH +#include "font2.c" +#endif +#if FULLSCREEN_PATCH +#include "fullscreen_x.c" +#endif +#if INVERT_PATCH +#include "invert.c" +#endif +#if KEYBOARDSELECT_PATCH +#include "keyboardselect_x.c" +#endif +#if OPENURLONCLICK_PATCH +#include "openurlonclick.c" +#endif +#if RIGHTCLICKTOPLUMB_PATCH +#include "rightclicktoplumb_x.c" +#endif +#if SIXEL_PATCH +#include "sixel_x.c" +#endif +#if ST_EMBEDDER_PATCH +#include "st_embedder_x.c" +#endif +#if XRESOURCES_PATCH +#include "xresources.c" +#endif \ No newline at end of file diff --git a/patch/x_include.h b/patch/x_include.h new file mode 100644 index 0000000..6505909 --- /dev/null +++ b/patch/x_include.h @@ -0,0 +1,40 @@ +/* Patches */ +#if ALPHA_PATCH +#include "alpha.h" +#endif +#if BACKGROUND_IMAGE_PATCH +#include "background_image_x.h" +#endif +#if BOXDRAW_PATCH +#include "boxdraw.h" +#endif +#if OPENCOPIED_PATCH +#include "opencopied.h" +#endif +#if FONT2_PATCH +#include "font2.h" +#endif +#if FULLSCREEN_PATCH +#include "fullscreen_x.h" +#endif +#if INVERT_PATCH +#include "invert.h" +#endif +#if KEYBOARDSELECT_PATCH +#include "keyboardselect_x.h" +#endif +#if NETWMICON_PATCH +#include "netwmicon.h" +#endif +#if RIGHTCLICKTOPLUMB_PATCH +#include "rightclicktoplumb_x.h" +#endif +#if ST_EMBEDDER_PATCH +#include "st_embedder_x.h" +#endif +#if XRESOURCES_PATCH +#include "xresources.h" +#endif +#if VIM_BROWSE_PATCH +#include "normalMode.h" +#endif diff --git a/patch/xresources.c b/patch/xresources.c new file mode 100644 index 0000000..ba3d985 --- /dev/null +++ b/patch/xresources.c @@ -0,0 +1,82 @@ +int +resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) +{ + char **sdst = dst; + int *idst = dst; + float *fdst = dst; + + char fullname[256]; + char fullclass[256]; + char *type; + XrmValue ret; + + snprintf(fullname, sizeof(fullname), "%s.%s", + opt_name ? opt_name : "st", name); + snprintf(fullclass, sizeof(fullclass), "%s.%s", + opt_class ? opt_class : "St", name); + fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; + + XrmGetResource(db, fullname, fullclass, &type, &ret); + if (ret.addr == NULL || strncmp("String", type, 64)) + return 1; + + switch (rtype) { + case STRING: + *sdst = ret.addr; + break; + case INTEGER: + *idst = strtoul(ret.addr, NULL, 10); + break; + case FLOAT: + *fdst = strtof(ret.addr, NULL); + break; + } + return 0; +} + +void +config_init(Display *dpy) +{ + char *resm; + XrmDatabase db; + ResourcePref *p; + + XrmInitialize(); + resm = XResourceManagerString(dpy); + if (!resm) + return; + + db = XrmGetStringDatabase(resm); + for (p = resources; p < resources + LEN(resources); p++) + resource_load(db, p->name, p->type, p->dst); +} + +#if XRESOURCES_RELOAD_PATCH +void +reload_config(int sig) +{ + /* Recreate a Display object to have up to date Xresources entries */ + Display *dpy; + if (!(dpy = XOpenDisplay(NULL))) + die("Can't open display\n"); + + config_init(dpy); + xloadcols(); + + /* nearly like zoomabs() */ + xunloadfonts(); + xloadfonts(font, 0); /* font <- config_init() */ + #if FONT2_PATCH + xloadsparefonts(); + #endif // FONT2_PATCH + cresize(0, 0); + redraw(); + xhints(); + + XCloseDisplay(dpy); + + /* from https://st.suckless.org/patches/xresources-with-reload-signal */ + /* triggers re-render if we're visible */ + ttywrite("\033[O", 3, 1); +} +#endif // XRESOURCES_RELOAD_PATCH diff --git a/patch/xresources.h b/patch/xresources.h new file mode 100644 index 0000000..c184852 --- /dev/null +++ b/patch/xresources.h @@ -0,0 +1,17 @@ +#include + +/* Xresources preferences */ +enum resource_type { + STRING = 0, + INTEGER = 1, + FLOAT = 2 +}; + +typedef struct { + char *name; + enum resource_type type; + void *dst; +} ResourcePref; + +int resource_load(XrmDatabase, char *, enum resource_type, void *); +void config_init(Display *dpy); diff --git a/patches.def.h b/patches.def.h new file mode 100644 index 0000000..1b294d7 --- /dev/null +++ b/patches.def.h @@ -0,0 +1,454 @@ +/* + * This file contains patch control flags. + * + * In principle you should be able to mix and match any patches + * you may want. In cases where patches are logically incompatible + * one patch may take precedence over the other as noted in the + * relevant descriptions. + */ + +/* Patches */ + +/* The alpha patch adds transparency for the terminal. + * You need to uncomment the corresponding line in config.mk to use the -lXrender library + * when including this patch. + * https://st.suckless.org/patches/alpha/ + */ +#define ALPHA_PATCH 1 + +/* The alpha focus highlight patch allows the user to specify two distinct opacity values or + * background colors in order to easily differentiate between focused and unfocused terminal + * windows. This depends on the alpha patch. + * https://github.com/juliusHuelsmann/st-focus/ + * https://st.suckless.org/patches/alpha_focus_highlight/ + */ +#define ALPHA_FOCUS_HIGHLIGHT_PATCH 0 + +/* Adds gradient transparency to st, depends on the alpha patch. + * https://st.suckless.org/patches/gradient/ + */ +#define ALPHA_GRADIENT_PATCH 0 + +/* This patch allows st to resize to any pixel size rather than snapping to character width/height. + * https://st.suckless.org/patches/anysize/ + */ +#define ANYSIZE_PATCH 1 + +/* A simple variant of the anysize patch that only changes the resize hints to allow the window to + * be resized to any size. + */ +#define ANYSIZE_SIMPLE_PATCH 0 + +/* Draws a background image in farbfeld format in place of the defaultbg color allowing for pseudo + * transparency. + * https://st.suckless.org/patches/background_image/ + */ +#define BACKGROUND_IMAGE_PATCH 0 + +/* This patch adds the ability to reload the background image config when a SIGUSR1 signal is + * received, e.g.: killall -USR1 st + * Depends on the BACKGROUND_IMAGE_PATCH. + */ +#define BACKGROUND_IMAGE_RELOAD_PATCH 0 + +/* This patch allows the use of a blinking cursor. + * Only cursor styles 0, 1, 3, 5, and 7 blink. Set cursorstyle accordingly. + * Cursor styles are defined here: + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 + * https://st.suckless.org/patches/blinking_cursor/ + */ +#define BLINKING_CURSOR_PATCH 0 + +/* By default bold text is rendered with a bold font in the bright variant of the current color. + * This patch makes bold text rendered simply as bold, leaving the color unaffected. + * https://st.suckless.org/patches/bold-is-not-bright/ + */ +#define BOLD_IS_NOT_BRIGHT_PATCH 0 + +/* This patch adds custom rendering of lines/blocks/braille characters for gapless alignment. + * https://st.suckless.org/patches/boxdraw/ + */ +#define BOXDRAW_PATCH 0 + +/* By default st only sets PRIMARY on selection. + * This patch makes st set CLIPBOARD on selection. + * https://st.suckless.org/patches/clipboard/ + */ +#define CLIPBOARD_PATCH 0 + +/* This patch allows st to be resized without cutting off text when the terminal window is + * made larger again. Text does not wrap when the terminal window is made smaller. + * + * The vim browse patch takes precedence over this patch. + * + * https://github.com/bakkeby/st-flexipatch/issues/34 + */ +#define COLUMNS_PATCH 1 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_PATCH 0 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. This variant also highlights the selected URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH 0 + +/* This patch adds support for CSI escape sequences 22 and 23, which save and + * restores the window title (for instance nvim does this when opening and closing). + * https://st.suckless.org/patches/csi_22_23/ + */ +#define CSI_22_23_PATCH 0 + +/* According to the specification (see link in BLINKING_CURSOR_PATCH) the "Set cursor style + * (DECSCUSR), VT520." escape sequences define both values of 0 and 1 as a blinking block, + * with 1 being the default. + * + * This patch allows the default cursor to be set when value 0 is used, as opposed to + * setting the cursor to a blinking block. + * + * This allows a command like this to restore the cursor to what st is configured with: + * $ echo -ne "\e[ q" + * + * While many terminal emulators do this it is not adhering to specification. xterm is an + * example terminal that sets a blinking block instead of the configured one, same as st. + */ +#define DEFAULT_CURSOR_PATCH 0 + +/* Return BS on pressing backspace and DEL on pressing the delete key. + * https://st.suckless.org/patches/delkey/ + */ +#define DELKEY_PATCH 0 + +/* This patch adds the option of disabling bold fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_BOLD_FONTS_PATCH 0 + +/* This patch adds the option of disabling italic fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ITALIC_FONTS_PATCH 0 + +/* This patch adds the option of disabling roman fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ROMAN_FONTS_PATCH 0 + +/* This patch makes the cursor color the inverse of the current cell color. + * https://st.suckless.org/patches/dynamic-cursor-color/ + */ +#define DYNAMIC_CURSOR_COLOR_PATCH 0 + +/* Reading and writing st's screen through a pipe, e.g. pass info to dmenu. + * https://st.suckless.org/patches/externalpipe/ + */ +#define EXTERNALPIPE_PATCH 0 + +/* This patch improves and extends the externalpipe patch in two ways: + * - it prevents the reset of the signal handler set on SIGCHILD, when + * the forked process that executes the external process exits and + * - it adds the externalpipein function to redirect the standard output + * of the external command to the slave size of the pty, that is, as if + * the external program had been manually executed on the terminal + * + * It can be used to send desired escape sequences to the terminal with a + * keyboard shortcut. The patch was created to make use of the dynamic-colors + * tool that uses the OSC escape sequences to change the colors of the terminal. + * + * This patch depends on EXTERNALPIPE_PATCH being enabled. + * + * https://github.com/sos4nt/dynamic-colors + * https://lists.suckless.org/hackers/2004/17218.html + */ +#define EXTERNALPIPEIN_PATCH 0 + +/* This patch allows command line applications to use all the fancy key combinations + * that are available to GUI applications. + * https://st.suckless.org/patches/fix_keyboard_input/ + */ +#define FIXKEYBOARDINPUT_PATCH 1 + +/* This patch allows you to add spare font besides the default. Some glyphs can be not present in + * the default font. For this glyphs st uses font-config and try to find them in font cache first. + * This patch append fonts defined in font2 variable to the beginning of the font cache. + * So they will be used first for glyphs that are absent in the default font. + * https://st.suckless.org/patches/font2/ + */ +#define FONT2_PATCH 0 + +/* This patch adds the ability to toggle st into fullscreen mode. + * Two key bindings are defined: F11 which is typical with other applications and Alt+Enter + * which matches the default xterm behavior. + * https://st.suckless.org/patches/fullscreen/ + */ +#define FULLSCREEN_PATCH 0 + +/* Hide the X cursor whenever a key is pressed and show it back when the mouse is moved in + * the terminal window. + * https://st.suckless.org/patches/hidecursor/ + */ +#define HIDECURSOR_PATCH 0 + +/* This patch hides the terminal cursor when the window loses focus (as opposed to showing a hollow + * cursor). + * https://www.reddit.com/r/suckless/comments/nvee8h/how_to_hide_cursor_in_st_is_there_a_patch_for_it/ + */ +#define HIDE_TERMINAL_CURSOR_PATCH 0 + +/* This patch adds a keybinding that lets you invert the current colorscheme of st. + * This provides a simple way to temporarily switch to a light colorscheme if you use a dark + * colorscheme or visa-versa. + * https://st.suckless.org/patches/invert/ + */ +#define INVERT_PATCH 0 + +/* Pressing the default binding Ctrl+Shift-i will popup dmenu, asking you to enter a unicode + * codepoint that will be converted to a glyph and then pushed to st. + * https://st.suckless.org/patches/iso14755/ + */ +#define ISO14755_PATCH 0 + +/* This patch allows you to select text on the terminal using keyboard shortcuts. + * https://st.suckless.org/patches/keyboard_select/ + */ +#define KEYBOARDSELECT_PATCH 0 + +/* This patch adds support for drawing ligatures using the Harfbuzz library to transform + * original text of a single line to a list of glyphs with ligatures included. + * This patch depends on the Harfbuzz library and headers to compile. + * You need to uncomment the corresponding lines in config.mk to use the harfbuzz library + * when including this patch. + * https://github.com/cog1to/st-ligatures + * https://st.suckless.org/patches/ligatures/ + */ +#define LIGATURES_PATCH 0 + +/* This patch makes st ignore terminal color attributes by forcing display of the default + * foreground and background colors only - making for a monochrome look. Idea ref. + * https://www.reddit.com/r/suckless/comments/ixbx6z/how_to_use_black_and_white_only_for_st/ + */ +#define MONOCHROME_PATCH 0 + +/* This patch sets the _NET_WM_ICON X property with a hardcoded icon for st. + * https://st.suckless.org/patches/netwmicon/ + */ +#define NETWMICON_PATCH 0 + +/* This patch allows you to spawn a new st terminal using Ctrl-Shift-Return. It will have the + * same CWD (current working directory) as the original st instance. + * https://st.suckless.org/patches/newterm/ + */ +#define NEWTERM_PATCH 0 + +/* This patch will set the _MOTIF_WM_HINTS property for the st window which, if the window manager + * respects it, will show the st window without window decorations. + * + * In dwm, if the decoration hints patch is applied, then the st window will start out without a + * border. In GNOME and KDE the window should start without a window title. + */ +#define NO_WINDOW_DECORATIONS_PATCH 0 + +/* Open contents of the clipboard in a user-defined browser. + * https://st.suckless.org/patches/open_copied_url/ + */ +#define OPENCOPIED_PATCH 0 + +/* This patch allows for URLs to be opened directly when you click on them. This may not work with + * all terminal applications. + * + * https://www.reddit.com/r/suckless/comments/cc83om/st_open_url/ + */ +#define OPENURLONCLICK_PATCH 0 + +/* This patch allows you to specify a border that is relative in size to the width of a cell + * in the terminal. + * https://st.suckless.org/patches/relativeborder/ + */ +#define RELATIVEBORDER_PATCH 0 + +/* This patch allows you to right-click on some selected text to send it to the plumbing + * program of choice, e.g. open a file, view an image, open a URL. + * https://st.suckless.org/patches/right_click_to_plumb/ + */ +#define RIGHTCLICKTOPLUMB_PATCH 0 + +/* Scroll back through terminal output using Shift+{PageUp, PageDown}. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_PATCH 1 + +/* Scroll back through terminal output using Shift+MouseWheel. + * This variant depends on SCROLLBACK_PATCH being enabled. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_MOUSE_PATCH 1 + +/* Scroll back through terminal output using mouse wheel (when not in MODE_ALTSCREEN). + * This variant depends on SCROLLBACK_PATCH being enabled. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_MOUSE_ALTSCREEN_PATCH 0 + +/* This is the single drawable buffer patch as outlined in the FAQ to get images + * in w3m to display. While this patch does not break the alpha patch it images + * are not shown in w3m if the alpha patch is applied. + */ +#define SINGLE_DRAWABLE_BUFFER_PATCH 0 + +/* This patch adds SIXEL graphics support for st. + * Note that patch/sixel.c/sixel_hls.c come from mintty, licensed under GPL. + * Known issues: + * - Rendering sixel graphics may cause unusual cursor placement, this is + * not specific to this variant of st - the same issue is present in + * the xterm implementation. This is likely an issue of sixel height + * not being detected correctly. + * + * Note that you need to uncomment the corresponding lines in config.mk when including this patch. + * This patch is incompatible with the W3M patch. + * + * https://gist.github.com/saitoha/70e0fdf22e3e8f63ce937c7f7da71809 + */ +#define SIXEL_PATCH 0 + +/* This patch allows clients to embed into the st window and is useful if you tend to + * start X applications from the terminal. For example: + * + * $ surf -e $WINDOWID + * + * The behavior is similar to Plan 9 where applications can take over windows. + * URL TBC + */ +#define ST_EMBEDDER_PATCH 0 + +/* Use inverted defaultbg/fg for selection when bg/fg are the same. + * https://st.suckless.org/patches/spoiler/ + */ +#define SPOILER_PATCH 0 + +/* This patch changes the mouse shape to the global default when the running program subscribes + * for mouse events, for instance, in programs like ranger and fzf. It emulates the behaviour + * shown by vte terminals like termite. + * https://st.suckless.org/patches/swapmouse/ + */ +#define SWAPMOUSE_PATCH 0 + +/* This patch adds synchronized-updates/application-sync support in st. + * This will have no effect except when an application uses the synchronized-update escape + * sequences. With this patch nearly all cursor flicker is eliminated in tmux, and tmux detects + * it automatically via terminfo. + * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * + * https://st.suckless.org/patches/sync/ + */ +#define SYNC_PATCH 0 + +/* Instead of a default X cursor, use the xterm cursor from your cursor theme. + * You need to uncomment the corresponding line in config.mk to use the -lXcursor library + * when including this patch. + * https://st.suckless.org/patches/themed_cursor/ + */ +#define THEMED_CURSOR_PATCH 0 + +/* Adds support for special underlines. + * + * Example test command: + * $ echo -e "\e[4:3m\e[58:5:10munderline\e[0m" + * ^ ^ ^ ^ ^- sets terminal color 10 + * | | | \- indicates that terminal colors should be used + * | | \- indicates that underline color is being set + * | \- sets underline style to curvy + * \- set underline + * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * + * https://st.suckless.org/patches/undercurl/ + */ +#define UNDERCURL_PATCH 0 + +/* Allows mouse scroll without modifier keys for regardless of alt screen using the external + * scroll program. + * https://st.suckless.org/patches/universcroll/ + */ +#define UNIVERSCROLL_PATCH 0 + +/* Use XftFontMatch in place of FcFontMatch. + * + * XftFontMatch calls XftDefaultSubstitute which configures various match properties according + * to the user's configured Xft defaults (xrdb) as well as according to the current display and + * screen. Most importantly, the screen DPI is computed [1]. Without this, st uses a "default" + * DPI of 75 [2]. + * + * [1]: https://cgit.freedesktop.org/xorg/lib/libXft/tree/src/xftdpy.c?id=libXft-2.3.2#n535 + * [2]: https://cgit.freedesktop.org/fontconfig/tree/src/fcdefault.c?id=2.11.1#n255 + * + * https://git.suckless.org/st/commit/528241aa3835e2f1f052abeeaf891737712955a0.html + */ +#define USE_XFTFONTMATCH_PATCH 0 + +/* Vertically center lines in the space available if you have set a larger chscale in config.h + * https://st.suckless.org/patches/vertcenter/ + */ +#define VERTCENTER_PATCH 0 + +/* The vim-browse patch offers the possibility to move through the terminal history-buffer, + * search for strings using VIM-like motions, operations and quantifiers. It overlays the + * screen with highlighted search results and displays the current operation / motions / search + * string in the bottom right corner. + * + * https://github.com/juliusHuelsmann/st-history-vim + * https://st.suckless.org/patches/vim_browse/ + */ +#define VIM_BROWSE_PATCH 0 + +/* Briefly inverts window content on terminal bell event. + * https://st.suckless.org/patches/visualbell/ + */ +#define VISUALBELL_1_PATCH 0 + +/* Adds support for w3m images. + * https://st.suckless.org/patches/w3m/ + */ +#define W3M_PATCH 0 + +/* Adds proper glyphs rendering in st allowing wide glyphs to be drawn as-is as opposed to + * smaller or cut glyphs being rendered. + * https://github.com/Dreomite/st/commit/e3b821dcb3511d60341dec35ee05a4a0abfef7f2 + * https://www.reddit.com/r/suckless/comments/jt90ai/update_support_for_proper_glyph_rendering_in_st/ + */ +#define WIDE_GLYPHS_PATCH 0 + +/* There is a known issue that Google's Variable Fonts (VF) can end up with letter spacing + * that is too wide in programs that use Xft, for example Inconsolata v3.000. + * + * This is intended as a temporary patch / hack until (if) this is fixed in the Xft library + * itself. + * + * https://github.com/googlefonts/Inconsolata/issues/42#issuecomment-737508890 + */ +#define WIDE_GLYPH_SPACING_PATCH 0 + +/* This patch allows user to specify the initial path st should use as the working directory. + * https://st.suckless.org/patches/workingdir/ + */ +#define WORKINGDIR_PATCH 0 + +/* This patch adds the ability to configure st via Xresources. At startup, st will read and + * apply the resources named in the resources[] array in config.h. + * https://st.suckless.org/patches/xresources/ + */ +#define XRESOURCES_PATCH 0 + +/* This patch adds the ability to reload the Xresources config when a SIGUSR1 signal is received + * e.g.: killall -USR1 st + * Depends on the XRESOURCES_PATCH. + */ +#define XRESOURCES_RELOAD_PATCH 0 diff --git a/patches.h b/patches.h new file mode 100644 index 0000000..b4913d8 --- /dev/null +++ b/patches.h @@ -0,0 +1,454 @@ +/* + * This file contains patch control flags. + * + * In principle you should be able to mix and match any patches + * you may want. In cases where patches are logically incompatible + * one patch may take precedence over the other as noted in the + * relevant descriptions. + */ + +/* Patches */ + +/* The alpha patch adds transparency for the terminal. + * You need to uncomment the corresponding line in config.mk to use the -lXrender library + * when including this patch. + * https://st.suckless.org/patches/alpha/ + */ +#define ALPHA_PATCH 1 + +/* The alpha focus highlight patch allows the user to specify two distinct opacity values or + * background colors in order to easily differentiate between focused and unfocused terminal + * windows. This depends on the alpha patch. + * https://github.com/juliusHuelsmann/st-focus/ + * https://st.suckless.org/patches/alpha_focus_highlight/ + */ +#define ALPHA_FOCUS_HIGHLIGHT_PATCH 0 + +/* Adds gradient transparency to st, depends on the alpha patch. + * https://st.suckless.org/patches/gradient/ + */ +#define ALPHA_GRADIENT_PATCH 0 + +/* This patch allows st to resize to any pixel size rather than snapping to character width/height. + * https://st.suckless.org/patches/anysize/ + */ +#define ANYSIZE_PATCH 1 + +/* A simple variant of the anysize patch that only changes the resize hints to allow the window to + * be resized to any size. + */ +#define ANYSIZE_SIMPLE_PATCH 0 + +/* Draws a background image in farbfeld format in place of the defaultbg color allowing for pseudo + * transparency. + * https://st.suckless.org/patches/background_image/ + */ +#define BACKGROUND_IMAGE_PATCH 0 + +/* This patch adds the ability to reload the background image config when a SIGUSR1 signal is + * received, e.g.: killall -USR1 st + * Depends on the BACKGROUND_IMAGE_PATCH. + */ +#define BACKGROUND_IMAGE_RELOAD_PATCH 0 + +/* This patch allows the use of a blinking cursor. + * Only cursor styles 0, 1, 3, 5, and 7 blink. Set cursorstyle accordingly. + * Cursor styles are defined here: + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 + * https://st.suckless.org/patches/blinking_cursor/ + */ +#define BLINKING_CURSOR_PATCH 1 + +/* By default bold text is rendered with a bold font in the bright variant of the current color. + * This patch makes bold text rendered simply as bold, leaving the color unaffected. + * https://st.suckless.org/patches/bold-is-not-bright/ + */ +#define BOLD_IS_NOT_BRIGHT_PATCH 0 + +/* This patch adds custom rendering of lines/blocks/braille characters for gapless alignment. + * https://st.suckless.org/patches/boxdraw/ + */ +#define BOXDRAW_PATCH 0 + +/* By default st only sets PRIMARY on selection. + * This patch makes st set CLIPBOARD on selection. + * https://st.suckless.org/patches/clipboard/ + */ +#define CLIPBOARD_PATCH 0 + +/* This patch allows st to be resized without cutting off text when the terminal window is + * made larger again. Text does not wrap when the terminal window is made smaller. + * + * The vim browse patch takes precedence over this patch. + * + * https://github.com/bakkeby/st-flexipatch/issues/34 + */ +#define COLUMNS_PATCH 1 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_PATCH 0 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. This variant also highlights the selected URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH 0 + +/* This patch adds support for CSI escape sequences 22 and 23, which save and + * restores the window title (for instance nvim does this when opening and closing). + * https://st.suckless.org/patches/csi_22_23/ + */ +#define CSI_22_23_PATCH 0 + +/* According to the specification (see link in BLINKING_CURSOR_PATCH) the "Set cursor style + * (DECSCUSR), VT520." escape sequences define both values of 0 and 1 as a blinking block, + * with 1 being the default. + * + * This patch allows the default cursor to be set when value 0 is used, as opposed to + * setting the cursor to a blinking block. + * + * This allows a command like this to restore the cursor to what st is configured with: + * $ echo -ne "\e[ q" + * + * While many terminal emulators do this it is not adhering to specification. xterm is an + * example terminal that sets a blinking block instead of the configured one, same as st. + */ +#define DEFAULT_CURSOR_PATCH 1 + +/* Return BS on pressing backspace and DEL on pressing the delete key. + * https://st.suckless.org/patches/delkey/ + */ +#define DELKEY_PATCH 0 + +/* This patch adds the option of disabling bold fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_BOLD_FONTS_PATCH 0 + +/* This patch adds the option of disabling italic fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ITALIC_FONTS_PATCH 0 + +/* This patch adds the option of disabling roman fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ROMAN_FONTS_PATCH 0 + +/* This patch makes the cursor color the inverse of the current cell color. + * https://st.suckless.org/patches/dynamic-cursor-color/ + */ +#define DYNAMIC_CURSOR_COLOR_PATCH 0 + +/* Reading and writing st's screen through a pipe, e.g. pass info to dmenu. + * https://st.suckless.org/patches/externalpipe/ + */ +#define EXTERNALPIPE_PATCH 0 + +/* This patch improves and extends the externalpipe patch in two ways: + * - it prevents the reset of the signal handler set on SIGCHILD, when + * the forked process that executes the external process exits and + * - it adds the externalpipein function to redirect the standard output + * of the external command to the slave size of the pty, that is, as if + * the external program had been manually executed on the terminal + * + * It can be used to send desired escape sequences to the terminal with a + * keyboard shortcut. The patch was created to make use of the dynamic-colors + * tool that uses the OSC escape sequences to change the colors of the terminal. + * + * This patch depends on EXTERNALPIPE_PATCH being enabled. + * + * https://github.com/sos4nt/dynamic-colors + * https://lists.suckless.org/hackers/2004/17218.html + */ +#define EXTERNALPIPEIN_PATCH 0 + +/* This patch allows command line applications to use all the fancy key combinations + * that are available to GUI applications. + * https://st.suckless.org/patches/fix_keyboard_input/ + */ +#define FIXKEYBOARDINPUT_PATCH 1 + +/* This patch allows you to add spare font besides the default. Some glyphs can be not present in + * the default font. For this glyphs st uses font-config and try to find them in font cache first. + * This patch append fonts defined in font2 variable to the beginning of the font cache. + * So they will be used first for glyphs that are absent in the default font. + * https://st.suckless.org/patches/font2/ + */ +#define FONT2_PATCH 1 + +/* This patch adds the ability to toggle st into fullscreen mode. + * Two key bindings are defined: F11 which is typical with other applications and Alt+Enter + * which matches the default xterm behavior. + * https://st.suckless.org/patches/fullscreen/ + */ +#define FULLSCREEN_PATCH 0 + +/* Hide the X cursor whenever a key is pressed and show it back when the mouse is moved in + * the terminal window. + * https://st.suckless.org/patches/hidecursor/ + */ +#define HIDECURSOR_PATCH 0 + +/* This patch hides the terminal cursor when the window loses focus (as opposed to showing a hollow + * cursor). + * https://www.reddit.com/r/suckless/comments/nvee8h/how_to_hide_cursor_in_st_is_there_a_patch_for_it/ + */ +#define HIDE_TERMINAL_CURSOR_PATCH 1 + +/* This patch adds a keybinding that lets you invert the current colorscheme of st. + * This provides a simple way to temporarily switch to a light colorscheme if you use a dark + * colorscheme or visa-versa. + * https://st.suckless.org/patches/invert/ + */ +#define INVERT_PATCH 0 + +/* Pressing the default binding Ctrl+Shift-i will popup dmenu, asking you to enter a unicode + * codepoint that will be converted to a glyph and then pushed to st. + * https://st.suckless.org/patches/iso14755/ + */ +#define ISO14755_PATCH 0 + +/* This patch allows you to select text on the terminal using keyboard shortcuts. + * https://st.suckless.org/patches/keyboard_select/ + */ +#define KEYBOARDSELECT_PATCH 0 + +/* This patch adds support for drawing ligatures using the Harfbuzz library to transform + * original text of a single line to a list of glyphs with ligatures included. + * This patch depends on the Harfbuzz library and headers to compile. + * You need to uncomment the corresponding lines in config.mk to use the harfbuzz library + * when including this patch. + * https://github.com/cog1to/st-ligatures + * https://st.suckless.org/patches/ligatures/ + */ +#define LIGATURES_PATCH 0 + +/* This patch makes st ignore terminal color attributes by forcing display of the default + * foreground and background colors only - making for a monochrome look. Idea ref. + * https://www.reddit.com/r/suckless/comments/ixbx6z/how_to_use_black_and_white_only_for_st/ + */ +#define MONOCHROME_PATCH 0 + +/* This patch sets the _NET_WM_ICON X property with a hardcoded icon for st. + * https://st.suckless.org/patches/netwmicon/ + */ +#define NETWMICON_PATCH 0 + +/* This patch allows you to spawn a new st terminal using Ctrl-Shift-Return. It will have the + * same CWD (current working directory) as the original st instance. + * https://st.suckless.org/patches/newterm/ + */ +#define NEWTERM_PATCH 0 + +/* This patch will set the _MOTIF_WM_HINTS property for the st window which, if the window manager + * respects it, will show the st window without window decorations. + * + * In dwm, if the decoration hints patch is applied, then the st window will start out without a + * border. In GNOME and KDE the window should start without a window title. + */ +#define NO_WINDOW_DECORATIONS_PATCH 0 + +/* Open contents of the clipboard in a user-defined browser. + * https://st.suckless.org/patches/open_copied_url/ + */ +#define OPENCOPIED_PATCH 0 + +/* This patch allows for URLs to be opened directly when you click on them. This may not work with + * all terminal applications. + * + * https://www.reddit.com/r/suckless/comments/cc83om/st_open_url/ + */ +#define OPENURLONCLICK_PATCH 0 + +/* This patch allows you to specify a border that is relative in size to the width of a cell + * in the terminal. + * https://st.suckless.org/patches/relativeborder/ + */ +#define RELATIVEBORDER_PATCH 0 + +/* This patch allows you to right-click on some selected text to send it to the plumbing + * program of choice, e.g. open a file, view an image, open a URL. + * https://st.suckless.org/patches/right_click_to_plumb/ + */ +#define RIGHTCLICKTOPLUMB_PATCH 0 + +/* Scroll back through terminal output using Shift+{PageUp, PageDown}. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_PATCH 1 + +/* Scroll back through terminal output using Shift+MouseWheel. + * This variant depends on SCROLLBACK_PATCH being enabled. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_MOUSE_PATCH 1 + +/* Scroll back through terminal output using mouse wheel (when not in MODE_ALTSCREEN). + * This variant depends on SCROLLBACK_PATCH being enabled. + * https://st.suckless.org/patches/scrollback/ + */ +#define SCROLLBACK_MOUSE_ALTSCREEN_PATCH 0 + +/* This is the single drawable buffer patch as outlined in the FAQ to get images + * in w3m to display. While this patch does not break the alpha patch it images + * are not shown in w3m if the alpha patch is applied. + */ +#define SINGLE_DRAWABLE_BUFFER_PATCH 0 + +/* This patch adds SIXEL graphics support for st. + * Note that patch/sixel.c/sixel_hls.c come from mintty, licensed under GPL. + * Known issues: + * - Rendering sixel graphics may cause unusual cursor placement, this is + * not specific to this variant of st - the same issue is present in + * the xterm implementation. This is likely an issue of sixel height + * not being detected correctly. + * + * Note that you need to uncomment the corresponding lines in config.mk when including this patch. + * This patch is incompatible with the W3M patch. + * + * https://gist.github.com/saitoha/70e0fdf22e3e8f63ce937c7f7da71809 + */ +#define SIXEL_PATCH 0 + +/* This patch allows clients to embed into the st window and is useful if you tend to + * start X applications from the terminal. For example: + * + * $ surf -e $WINDOWID + * + * The behavior is similar to Plan 9 where applications can take over windows. + * URL TBC + */ +#define ST_EMBEDDER_PATCH 1 + +/* Use inverted defaultbg/fg for selection when bg/fg are the same. + * https://st.suckless.org/patches/spoiler/ + */ +#define SPOILER_PATCH 0 + +/* This patch changes the mouse shape to the global default when the running program subscribes + * for mouse events, for instance, in programs like ranger and fzf. It emulates the behaviour + * shown by vte terminals like termite. + * https://st.suckless.org/patches/swapmouse/ + */ +#define SWAPMOUSE_PATCH 0 + +/* This patch adds synchronized-updates/application-sync support in st. + * This will have no effect except when an application uses the synchronized-update escape + * sequences. With this patch nearly all cursor flicker is eliminated in tmux, and tmux detects + * it automatically via terminfo. + * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * + * https://st.suckless.org/patches/sync/ + */ +#define SYNC_PATCH 0 + +/* Instead of a default X cursor, use the xterm cursor from your cursor theme. + * You need to uncomment the corresponding line in config.mk to use the -lXcursor library + * when including this patch. + * https://st.suckless.org/patches/themed_cursor/ + */ +#define THEMED_CURSOR_PATCH 0 + +/* Adds support for special underlines. + * + * Example test command: + * $ echo -e "\e[4:3m\e[58:5:10munderline\e[0m" + * ^ ^ ^ ^ ^- sets terminal color 10 + * | | | \- indicates that terminal colors should be used + * | | \- indicates that underline color is being set + * | \- sets underline style to curvy + * \- set underline + * + * Note: this patch alters st.info to promote support for extra escape sequences, which can + * potentially cause application misbehaviour if you do not use this patch. Try removing or + * commenting out the corresponding line in st.info if this is causing issues. + * + * https://st.suckless.org/patches/undercurl/ + */ +#define UNDERCURL_PATCH 0 + +/* Allows mouse scroll without modifier keys for regardless of alt screen using the external + * scroll program. + * https://st.suckless.org/patches/universcroll/ + */ +#define UNIVERSCROLL_PATCH 0 + +/* Use XftFontMatch in place of FcFontMatch. + * + * XftFontMatch calls XftDefaultSubstitute which configures various match properties according + * to the user's configured Xft defaults (xrdb) as well as according to the current display and + * screen. Most importantly, the screen DPI is computed [1]. Without this, st uses a "default" + * DPI of 75 [2]. + * + * [1]: https://cgit.freedesktop.org/xorg/lib/libXft/tree/src/xftdpy.c?id=libXft-2.3.2#n535 + * [2]: https://cgit.freedesktop.org/fontconfig/tree/src/fcdefault.c?id=2.11.1#n255 + * + * https://git.suckless.org/st/commit/528241aa3835e2f1f052abeeaf891737712955a0.html + */ +#define USE_XFTFONTMATCH_PATCH 0 + +/* Vertically center lines in the space available if you have set a larger chscale in config.h + * https://st.suckless.org/patches/vertcenter/ + */ +#define VERTCENTER_PATCH 0 + +/* The vim-browse patch offers the possibility to move through the terminal history-buffer, + * search for strings using VIM-like motions, operations and quantifiers. It overlays the + * screen with highlighted search results and displays the current operation / motions / search + * string in the bottom right corner. + * + * https://github.com/juliusHuelsmann/st-history-vim + * https://st.suckless.org/patches/vim_browse/ + */ +#define VIM_BROWSE_PATCH 0 + +/* Briefly inverts window content on terminal bell event. + * https://st.suckless.org/patches/visualbell/ + */ +#define VISUALBELL_1_PATCH 0 + +/* Adds support for w3m images. + * https://st.suckless.org/patches/w3m/ + */ +#define W3M_PATCH 0 + +/* Adds proper glyphs rendering in st allowing wide glyphs to be drawn as-is as opposed to + * smaller or cut glyphs being rendered. + * https://github.com/Dreomite/st/commit/e3b821dcb3511d60341dec35ee05a4a0abfef7f2 + * https://www.reddit.com/r/suckless/comments/jt90ai/update_support_for_proper_glyph_rendering_in_st/ + */ +#define WIDE_GLYPHS_PATCH 0 + +/* There is a known issue that Google's Variable Fonts (VF) can end up with letter spacing + * that is too wide in programs that use Xft, for example Inconsolata v3.000. + * + * This is intended as a temporary patch / hack until (if) this is fixed in the Xft library + * itself. + * + * https://github.com/googlefonts/Inconsolata/issues/42#issuecomment-737508890 + */ +#define WIDE_GLYPH_SPACING_PATCH 0 + +/* This patch allows user to specify the initial path st should use as the working directory. + * https://st.suckless.org/patches/workingdir/ + */ +#define WORKINGDIR_PATCH 0 + +/* This patch adds the ability to configure st via Xresources. At startup, st will read and + * apply the resources named in the resources[] array in config.h. + * https://st.suckless.org/patches/xresources/ + */ +#define XRESOURCES_PATCH 0 + +/* This patch adds the ability to reload the Xresources config when a SIGUSR1 signal is received + * e.g.: killall -USR1 st + * Depends on the XRESOURCES_PATCH. + */ +#define XRESOURCES_RELOAD_PATCH 0 diff --git a/sixel.c b/sixel.c new file mode 100644 index 0000000..2c382f4 --- /dev/null +++ b/sixel.c @@ -0,0 +1,616 @@ +// sixel.c (part of mintty) +// originally written by kmiya@cluti (https://github.com/saitoha/sixel/blob/master/fromsixel.c) +// Licensed under the terms of the GNU General Public License v3 or later. + +#include +#include /* memcpy */ + +#include "sixel.h" +#include "sixel_hls.h" + +#define SIXEL_RGB(r, g, b) ((r) + ((g) << 8) + ((b) << 16)) +#define SIXEL_PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m)) +#define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100)) + +static sixel_color_t const sixel_default_color_table[] = { + SIXEL_XRGB( 0, 0, 0), /* 0 Black */ + SIXEL_XRGB(20, 20, 80), /* 1 Blue */ + SIXEL_XRGB(80, 13, 13), /* 2 Red */ + SIXEL_XRGB(20, 80, 20), /* 3 Green */ + SIXEL_XRGB(80, 20, 80), /* 4 Magenta */ + SIXEL_XRGB(20, 80, 80), /* 5 Cyan */ + SIXEL_XRGB(80, 80, 20), /* 6 Yellow */ + SIXEL_XRGB(53, 53, 53), /* 7 Gray 50% */ + SIXEL_XRGB(26, 26, 26), /* 8 Gray 25% */ + SIXEL_XRGB(33, 33, 60), /* 9 Blue* */ + SIXEL_XRGB(60, 26, 26), /* 10 Red* */ + SIXEL_XRGB(33, 60, 33), /* 11 Green* */ + SIXEL_XRGB(60, 33, 60), /* 12 Magenta* */ + SIXEL_XRGB(33, 60, 60), /* 13 Cyan* */ + SIXEL_XRGB(60, 60, 33), /* 14 Yellow* */ + SIXEL_XRGB(80, 80, 80), /* 15 Gray 75% */ +}; + +static int +set_default_color(sixel_image_t *image) +{ + int i; + int n; + int r; + int g; + int b; + + /* palette initialization */ + for (n = 1; n < 17; n++) { + image->palette[n] = sixel_default_color_table[n - 1]; + } + + /* colors 17-232 are a 6x6x6 color cube */ + for (r = 0; r < 6; r++) { + for (g = 0; g < 6; g++) { + for (b = 0; b < 6; b++) { + image->palette[n++] = SIXEL_RGB(r * 51, g * 51, b * 51); + } + } + } + + /* colors 233-256 are a grayscale ramp, intentionally leaving out */ + for (i = 0; i < 24; i++) { + image->palette[n++] = SIXEL_RGB(i * 11, i * 11, i * 11); + } + + for (; n < DECSIXEL_PALETTE_MAX; n++) { + image->palette[n] = SIXEL_RGB(255, 255, 255); + } + + return (0); +} + +static int +sixel_image_init( + sixel_image_t *image, + int width, + int height, + int fgcolor, + int bgcolor, + int use_private_register) +{ + int status = (-1); + size_t size; + + size = (size_t)(width * height) * sizeof(sixel_color_no_t); + image->width = width; + image->height = height; + image->data = (sixel_color_no_t *)malloc(size); + image->ncolors = 2; + image->use_private_register = use_private_register; + + if (image->data == NULL) { + status = (-1); + goto end; + } + memset(image->data, 0, size); + + image->palette[0] = bgcolor; + + if (image->use_private_register) + image->palette[1] = fgcolor; + + image->palette_modified = 0; + + status = (0); + +end: + return status; +} + + +static int +image_buffer_resize( + sixel_image_t *image, + int width, + int height) +{ + int status = (-1); + size_t size; + sixel_color_no_t *alt_buffer; + int n; + int min_height; + + size = (size_t)(width * height) * sizeof(sixel_color_no_t); + alt_buffer = (sixel_color_no_t *)malloc(size); + if (alt_buffer == NULL) { + /* free source image */ + free(image->data); + image->data = NULL; + status = (-1); + goto end; + } + + min_height = height > image->height ? image->height: height; + if (width > image->width) { /* if width is extended */ + for (n = 0; n < min_height; ++n) { + /* copy from source image */ + memcpy(alt_buffer + width * n, + image->data + image->width * n, + (size_t)image->width * sizeof(sixel_color_no_t)); + /* fill extended area with background color */ + memset(alt_buffer + width * n + image->width, + 0, + (size_t)(width - image->width) * sizeof(sixel_color_no_t)); + } + } else { + for (n = 0; n < min_height; ++n) { + /* copy from source image */ + memcpy(alt_buffer + width * n, + image->data + image->width * n, + (size_t)width * sizeof(sixel_color_no_t)); + } + } + + if (height > image->height) { /* if height is extended */ + /* fill extended area with background color */ + memset(alt_buffer + width * image->height, + 0, + (size_t)(width * (height - image->height)) * sizeof(sixel_color_no_t)); + } + + /* free source image */ + free(image->data); + + image->data = alt_buffer; + image->width = width; + image->height = height; + + status = (0); + +end: + return status; +} + +static void +sixel_image_deinit(sixel_image_t *image) +{ + free(image->data); + image->data = NULL; +} + +int +sixel_parser_init(sixel_state_t *st, + sixel_color_t fgcolor, sixel_color_t bgcolor, + unsigned char use_private_register, + int cell_width, int cell_height) +{ + int status = (-1); + + st->state = PS_DECSIXEL; + st->pos_x = 0; + st->pos_y = 0; + st->max_x = 0; + st->max_y = 0; + st->attributed_pan = 2; + st->attributed_pad = 1; + st->attributed_ph = 0; + st->attributed_pv = 0; + st->repeat_count = 1; + st->color_index = 16; + st->grid_width = cell_width; + st->grid_height = cell_height; + st->nparams = 0; + st->param = 0; + + /* buffer initialization */ + status = sixel_image_init(&st->image, 1, 1, fgcolor, bgcolor, use_private_register); + + return status; +} + +int +sixel_parser_set_default_color(sixel_state_t *st) +{ + return set_default_color(&st->image); +} + +int +sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels) +{ + int status = (-1); + int sx; + int sy; + sixel_image_t *image = &st->image; + int x, y; + sixel_color_no_t *src; + unsigned char *dst; + int color; + + if (++st->max_x < st->attributed_ph) + st->max_x = st->attributed_ph; + + if (++st->max_y < st->attributed_pv) + st->max_y = st->attributed_pv; + + sx = (st->max_x + st->grid_width - 1) / st->grid_width * st->grid_width; + sy = (st->max_y + st->grid_height - 1) / st->grid_height * st->grid_height; + + if (image->width > sx || image->height > sy) { + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + + if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) { + status = set_default_color(image); + if (status < 0) + goto end; + } + + src = st->image.data; + dst = pixels; + for (y = 0; y < st->image.height; ++y) { + for (x = 0; x < st->image.width; ++x) { + color = st->image.palette[*src++]; + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + *dst++ = 255; /* a */ + } + /* fill right padding with bgcolor */ + for (; x < st->image.width; ++x) { + color = st->image.palette[0]; /* bgcolor */ + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + dst++; /* a */ + } + } + /* fill bottom padding with bgcolor */ + for (; y < st->image.height; ++y) { + for (x = 0; x < st->image.width; ++x) { + color = st->image.palette[0]; /* bgcolor */ + *dst++ = color >> 16 & 0xff; /* b */ + *dst++ = color >> 8 & 0xff; /* g */ + *dst++ = color >> 0 & 0xff; /* r */ + dst++; /* a */ + } + } + + status = (0); + +end: + return status; +} + +/* convert sixel data into indexed pixel bytes and palette data */ +int +sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) +{ + int status = (-1); + int n; + int i; + int x; + int y; + int bits; + int sixel_vertical_mask; + int sx; + int sy; + int c; + int pos; + unsigned char *p0 = p; + sixel_image_t *image = &st->image; + + if (! image->data) + goto end; + + while (p < p0 + len) { + switch (st->state) { + case PS_ESC: + goto end; + + case PS_DECSIXEL: + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '"': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGRA; + p++; + break; + case '!': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGRI; + p++; + break; + case '#': + st->param = 0; + st->nparams = 0; + st->state = PS_DECGCI; + p++; + break; + case '$': + /* DECGCR Graphics Carriage Return */ + st->pos_x = 0; + p++; + break; + case '-': + /* DECGNL Graphics Next Line */ + st->pos_x = 0; + if (st->pos_y < DECSIXEL_HEIGHT_MAX - 5 - 6) + st->pos_y += 6; + else + st->pos_y = DECSIXEL_HEIGHT_MAX + 1; + p++; + break; + default: + if (*p >= '?' && *p <= '~') { /* sixel characters */ + if ((image->width < (st->pos_x + st->repeat_count) || image->height < (st->pos_y + 6)) + && image->width < DECSIXEL_WIDTH_MAX && image->height < DECSIXEL_HEIGHT_MAX) { + sx = image->width * 2; + sy = image->height * 2; + while (sx < (st->pos_x + st->repeat_count) || sy < (st->pos_y + 6)) { + sx *= 2; + sy *= 2; + } + + if (sx > DECSIXEL_WIDTH_MAX) + sx = DECSIXEL_WIDTH_MAX; + if (sy > DECSIXEL_HEIGHT_MAX) + sy = DECSIXEL_HEIGHT_MAX; + + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + + if (st->color_index > image->ncolors) + image->ncolors = st->color_index; + + if (st->pos_x + st->repeat_count > image->width) + st->repeat_count = image->width - st->pos_x; + + if (st->repeat_count > 0 && st->pos_y - 5 < image->height) { + bits = *p - '?'; + if (bits != 0) { + sixel_vertical_mask = 0x01; + if (st->repeat_count <= 1) { + for (i = 0; i < 6; i++) { + if ((bits & sixel_vertical_mask) != 0) { + pos = image->width * (st->pos_y + i) + st->pos_x; + image->data[pos] = st->color_index; + if (st->max_x < st->pos_x) + st->max_x = st->pos_x; + if (st->max_y < (st->pos_y + i)) + st->max_y = st->pos_y + i; + } + sixel_vertical_mask <<= 1; + } + } else { + /* st->repeat_count > 1 */ + for (i = 0; i < 6; i++) { + if ((bits & sixel_vertical_mask) != 0) { + c = sixel_vertical_mask << 1; + for (n = 1; (i + n) < 6; n++) { + if ((bits & c) == 0) + break; + c <<= 1; + } + for (y = st->pos_y + i; y < st->pos_y + i + n; ++y) { + for (x = st->pos_x; x < st->pos_x + st->repeat_count; ++x) + image->data[image->width * y + x] = st->color_index; + } + if (st->max_x < (st->pos_x + st->repeat_count - 1)) + st->max_x = st->pos_x + st->repeat_count - 1; + if (st->max_y < (st->pos_y + i + n - 1)) + st->max_y = st->pos_y + i + n - 1; + i += (n - 1); + sixel_vertical_mask <<= (n - 1); + } + sixel_vertical_mask <<= 1; + } + } + } + } + if (st->repeat_count > 0) + st->pos_x += st->repeat_count; + st->repeat_count = 1; + } + p++; + break; + } + break; + + case PS_DECGRA: + /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + case ';': + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + p++; + break; + default: + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + if (st->nparams > 0) + st->attributed_pad = st->params[0]; + if (st->nparams > 1) + st->attributed_pan = st->params[1]; + if (st->nparams > 2 && st->params[2] > 0) + st->attributed_ph = st->params[2]; + if (st->nparams > 3 && st->params[3] > 0) + st->attributed_pv = st->params[3]; + + if (st->attributed_pan <= 0) + st->attributed_pan = 1; + if (st->attributed_pad <= 0) + st->attributed_pad = 1; + + if (image->width < st->attributed_ph || + image->height < st->attributed_pv) { + sx = st->attributed_ph; + if (image->width > st->attributed_ph) + sx = image->width; + + sy = st->attributed_pv; + if (image->height > st->attributed_pv) + sy = image->height; + + sx = (sx + st->grid_width - 1) / st->grid_width * st->grid_width; + sy = (sy + st->grid_height - 1) / st->grid_height * st->grid_height; + + if (sx > DECSIXEL_WIDTH_MAX) + sx = DECSIXEL_WIDTH_MAX; + if (sy > DECSIXEL_HEIGHT_MAX) + sy = DECSIXEL_HEIGHT_MAX; + + status = image_buffer_resize(image, sx, sy); + if (status < 0) + goto end; + } + st->state = PS_DECSIXEL; + st->param = 0; + st->nparams = 0; + } + break; + + case PS_DECGRI: + /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + default: + st->repeat_count = st->param; + if (st->repeat_count == 0) + st->repeat_count = 1; + st->state = PS_DECSIXEL; + st->param = 0; + st->nparams = 0; + break; + } + break; + + case PS_DECGCI: + /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ + switch (*p) { + case '\x1b': + st->state = PS_ESC; + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + st->param = st->param * 10 + *p - '0'; + if (st->param > DECSIXEL_PARAMVALUE_MAX) + st->param = DECSIXEL_PARAMVALUE_MAX; + p++; + break; + case ';': + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + p++; + break; + default: + st->state = PS_DECSIXEL; + if (st->nparams < DECSIXEL_PARAMS_MAX) + st->params[st->nparams++] = st->param; + st->param = 0; + + if (st->nparams > 0) { + st->color_index = 1 + st->params[0]; /* offset 1(background color) added */ + if (st->color_index < 0) + st->color_index = 0; + else if (st->color_index >= DECSIXEL_PALETTE_MAX) + st->color_index = DECSIXEL_PALETTE_MAX - 1; + } + + if (st->nparams > 4) { + st->image.palette_modified = 1; + if (st->params[1] == 1) { + /* HLS */ + if (st->params[2] > 360) + st->params[2] = 360; + if (st->params[3] > 100) + st->params[3] = 100; + if (st->params[4] > 100) + st->params[4] = 100; + image->palette[st->color_index] + = hls_to_rgb(st->params[2], st->params[3], st->params[4]); + } else if (st->params[1] == 2) { + /* RGB */ + if (st->params[2] > 100) + st->params[2] = 100; + if (st->params[3] > 100) + st->params[3] = 100; + if (st->params[4] > 100) + st->params[4] = 100; + image->palette[st->color_index] + = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]); + } + } + break; + } + break; + default: + break; + } + } + + status = (0); + +end: + return status; +} + +void +sixel_parser_deinit(sixel_state_t *st) +{ + if (st) + sixel_image_deinit(&st->image); +} diff --git a/sixel.h b/sixel.h new file mode 100644 index 0000000..8a05c44 --- /dev/null +++ b/sixel.h @@ -0,0 +1,58 @@ +#ifndef SIXEL_H +#define SIXEL_H + +#define DECSIXEL_PARAMS_MAX 16 +#define DECSIXEL_PALETTE_MAX 1024 +#define DECSIXEL_PARAMVALUE_MAX 65535 +#define DECSIXEL_WIDTH_MAX 4096 +#define DECSIXEL_HEIGHT_MAX 4096 + +typedef unsigned short sixel_color_no_t; +typedef unsigned int sixel_color_t; + +typedef struct sixel_image_buffer { + sixel_color_no_t *data; + int width; + int height; + sixel_color_t palette[DECSIXEL_PALETTE_MAX]; + sixel_color_no_t ncolors; + int palette_modified; + int use_private_register; +} sixel_image_t; + +typedef enum parse_state { + PS_ESC = 1, /* ESC */ + PS_DECSIXEL = 2, /* DECSIXEL body part ", $, -, ? ... ~ */ + PS_DECGRA = 3, /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ + PS_DECGRI = 4, /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + PS_DECGCI = 5, /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ +} parse_state_t; + +typedef struct parser_context { + parse_state_t state; + int pos_x; + int pos_y; + int max_x; + int max_y; + int attributed_pan; + int attributed_pad; + int attributed_ph; + int attributed_pv; + int repeat_count; + int color_index; + int bgindex; + int grid_width; + int grid_height; + int param; + int nparams; + int params[DECSIXEL_PARAMS_MAX]; + sixel_image_t image; +} sixel_state_t; + +int sixel_parser_init(sixel_state_t *st, sixel_color_t fgcolor, sixel_color_t bgcolor, unsigned char use_private_register, int cell_width, int cell_height); +int sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len); +int sixel_parser_set_default_color(sixel_state_t *st); +int sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels); +void sixel_parser_deinit(sixel_state_t *st); + +#endif diff --git a/sixel_hls.c b/sixel_hls.c new file mode 100644 index 0000000..4f157b2 --- /dev/null +++ b/sixel_hls.c @@ -0,0 +1,115 @@ +// sixel.c (part of mintty) +// this function is derived from a part of graphics.c +// in Xterm pl#310 originally written by Ross Combs. +// +// Copyright 2013,2014 by Ross Combs +// +// All Rights Reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the above copyright +// holders shall not be used in advertising or otherwise to promote the +// sale, use or other dealings in this Software without prior written +// authorization. + +#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) + (b)) + +int +hls_to_rgb(int hue, int lum, int sat) +{ + double hs = (hue + 240) % 360; + double hv = hs / 360.0; + double lv = lum / 100.0; + double sv = sat / 100.0; + double c, x, m, c2; + double r1, g1, b1; + int r, g, b; + int hpi; + + if (sat == 0) { + r = g = b = lum * 255 / 100; + return SIXEL_RGB(r, g, b); + } + + if ((c2 = ((2.0 * lv) - 1.0)) < 0.0) { + c2 = -c2; + } + c = (1.0 - c2) * sv; + hpi = (int) (hv * 6.0); + x = (hpi & 1) ? c : 0.0; + m = lv - 0.5 * c; + + switch (hpi) { + case 0: + r1 = c; + g1 = x; + b1 = 0.0; + break; + case 1: + r1 = x; + g1 = c; + b1 = 0.0; + break; + case 2: + r1 = 0.0; + g1 = c; + b1 = x; + break; + case 3: + r1 = 0.0; + g1 = x; + b1 = c; + break; + case 4: + r1 = x; + g1 = 0.0; + b1 = c; + break; + case 5: + r1 = c; + g1 = 0.0; + b1 = x; + break; + default: + return SIXEL_RGB(255, 255, 255); + } + + r = (int) ((r1 + m) * 100.0 + 0.5); + g = (int) ((g1 + m) * 100.0 + 0.5); + b = (int) ((b1 + m) * 100.0 + 0.5); + + if (r < 0) { + r = 0; + } else if (r > 100) { + r = 100; + } + if (g < 0) { + g = 0; + } else if (g > 100) { + g = 100; + } + if (b < 0) { + b = 0; + } else if (b > 100) { + b = 100; + } + return SIXEL_RGB(r * 255 / 100, g * 255 / 100, b * 255 / 100); +} diff --git a/sixel_hls.h b/sixel_hls.h new file mode 100644 index 0000000..6176589 --- /dev/null +++ b/sixel_hls.h @@ -0,0 +1,7 @@ +/* + * Primary color hues: + * blue: 0 degrees + * red: 120 degrees + * green: 240 degrees + */ +int hls_to_rgb(int hue, int lum, int sat); diff --git a/st b/st index f9f4fd9d374aa737fc7b996d159b897c8ab77a75..120a28475cf27c7f94401309ce9d8901c7fb45d3 100755 GIT binary patch literal 143800 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 literal 122376 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�Kno?`;esD{Ujt9X>< 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 vE0EAn?`n5Cev*Gv+p(1L_qfe}ss2zeuthtRm +#endif // VIM_BROWSE_PATCH + +#if KEYBOARDSELECT_PATCH +#include +#include +#endif // KEYBOARDSELECT_PATCH + +#if SIXEL_PATCH +#include "sixel.h" +#endif // SIXEL_PATCH + #if defined(__linux) #include #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) @@ -33,19 +46,22 @@ #define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 +#if UNDERCURL_PATCH +#define CAR_PER_ARG 4 +#endif // UNDERCURL_PATCH #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ -#define HISTSIZE 2000 /* macros */ -#define IS_SET(flag) ((term.mode & (flag)) != 0) -#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) -#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) -#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (u && wcschr(worddelimiters, u)) -#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ - term.scr + HISTSIZE + 1) % HISTSIZE] : \ - term.line[(y) - term.scr]) +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#if VIM_BROWSE_PATCH +static inline int max(int a, int b) { return a > b ? a : b; } +static inline int min(int a, int b) { return a < b ? a : b; } +#endif // VIM_BROWSE_PATCH enum term_mode { MODE_WRAP = 1 << 0, @@ -55,6 +71,9 @@ enum term_mode { MODE_ECHO = 1 << 4, MODE_PRINT = 1 << 5, MODE_UTF8 = 1 << 6, + #if SIXEL_PATCH + MODE_SIXEL = 1 << 7, + #endif // SIXEL_PATCH }; enum cursor_movement { @@ -86,19 +105,18 @@ enum escape_state { ESC_STR_END = 16, /* a final string was encountered */ ESC_TEST = 32, /* Enter in test mode */ ESC_UTF8 = 64, + #if SIXEL_PATCH + ESC_DCS =128, + #endif // SIXEL_PATCH }; -typedef struct { - Glyph attr; /* current char attributes */ - int x; - int y; - char state; -} TCursor; - typedef struct { int mode; int type; int snap; + #if VIM_BROWSE_PATCH + int swap; + #endif // VIM_BROWSE_PATCH /* * Selection variables: * nb – normalized coordinates of the beginning of the selection @@ -113,31 +131,6 @@ typedef struct { int alt; } Selection; -/* Internal representation of the screen */ -typedef struct { - int row; /* nb row */ - int col; /* nb col */ - int maxcol; - Line *line; /* screen */ - Line *alt; /* alternate screen */ - Line hist[HISTSIZE]; /* history buffer */ - int histi; /* history index */ - int scr; /* scroll back */ - int *dirty; /* dirtyness of lines */ - TCursor c; /* cursor */ - int ocx; /* old cursor col */ - int ocy; /* old cursor row */ - int top; /* top scroll limit */ - int bot; /* bottom scroll limit */ - int mode; /* terminal mode flags */ - int esc; /* escape state flags */ - char trantbl[4]; /* charset table translation */ - int charset; /* current charset */ - int icharset; /* selected charset for sequence */ - int *tabs; - Rune lastc; /* last printed char outside of sequence, 0 if control */ -} Term; - /* CSI Escape sequence structs */ /* ESC '[' [[ [] [;]] []] */ typedef struct { @@ -147,6 +140,9 @@ typedef struct { int arg[ESC_ARG_SIZ]; int narg; /* nb of args */ char mode[2]; + #if UNDERCURL_PATCH + int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ + #endif // UNDERCURL_PATCH } CSIEscape; /* STR Escape sequence structs */ @@ -167,9 +163,13 @@ static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); +#if UNDERCURL_PATCH +static void readcolonargs(char **, int, int[][CAR_PER_ARG]); +#endif // UNDERCURL_PATCH static void csiparse(void); static void csireset(void); -static void osc_color_response(int, int, int); +static void osc4_color_response(int num); +static void osc_color_response(int index, int num); static int eschandle(uchar); static void strdump(void); static void strhandle(void); @@ -187,14 +187,20 @@ static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); static int tlinelen(int); +#if !VIM_BROWSE_PATCH static void tmoveto(int, int); +#endif // VIM_BROWSE_PATCH static void tmoveato(int, int); static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); +#if SCROLLBACK_PATCH static void tscrollup(int, int, int); -static void tscrolldown(int, int, int); +#else +static void tscrollup(int, int); +#endif // SCROLLBACK_PATCH +static void tscrolldown(int, int); static void tsetattr(const int *, int); static void tsetchar(Rune, const Glyph *, int, int); static void tsetdirt(int, int); @@ -202,19 +208,17 @@ static void tsetscroll(int, int); static void tswapscreen(void); static void tsetmode(int, int, const int *, int); static int twrite(const char *, int, int); -static void tfulldirt(void); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); static int32_t tdefcolor(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); - -static void drawregion(int, int, int, int); - static void selnormalize(void); static void selscroll(int, int); +#if !VIM_BROWSE_PATCH static void selsnap(int *, int *, int); +#endif // VIM_BROWSE_PATCH static size_t utf8decode(const char *, Rune *, size_t); static Rune utf8decodebyte(char, size_t *); @@ -227,19 +231,36 @@ static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -static Term term; static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; static int iofd = 1; static int cmdfd; +#if EXTERNALPIPEIN_PATCH && EXTERNALPIPE_PATCH +static int csdfd; +#endif // EXTERNALPIPEIN_PATCH static pid_t pid; +#if SIXEL_PATCH +sixel_state_t sixel_st; +#endif // SIXEL_PATCH static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +#if VIM_BROWSE_PATCH +int buffCols; +extern int const buffSize; +int histOp, histMode, histOff, insertOff, altToggle, *mark; +Line *buf = NULL; +static TCursor c[3]; +static inline int rows() { return IS_SET(MODE_ALTSCREEN) ? term.row : buffSize;} +static inline int rangeY(int i) { while (i < 0) i += rows(); return i % rows();} +#endif // VIM_BROWSE_PATCH + +#include "patch/st_include.h" + ssize_t xwrite(int fd, const char *s, size_t len) { @@ -281,7 +302,6 @@ char * xstrdup(const char *s) { char *p; - if ((p = strdup(s)) == NULL) die("strdup: %s\n", strerror(errno)); @@ -417,15 +437,135 @@ tlinelen(int y) { int i = term.col; + #if SCROLLBACK_PATCH if (TLINE(y)[i - 1].mode & ATTR_WRAP) return i; while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + #else + if (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') --i; + #endif // SCROLLBACK_PATCH return i; } +#if VIM_BROWSE_PATCH +void historyOpToggle(int start, int paint) { + if ((!histOp == !(histOp + start)) && ((histOp += start) || 1)) return; + if (histMode && paint && (!IS_SET(MODE_ALTSCREEN) || altToggle)) draw(); + tcursor(CURSOR_SAVE); + histOp += start; + if (histMode && altToggle) { + tswapscreen(); + memset(term.dirty,0,sizeof(*term.dirty)*term.row); + } + tcursor(CURSOR_LOAD); + *(!IS_SET(MODE_ALTSCREEN)?&term.line:&term.alt)=&buf[histOp?histOff:insertOff]; +} + +void historyModeToggle(int start) { + if (!(histMode = (histOp = !!start))) { + selnormalize(); + tfulldirt(); + } else { + tcursor(CURSOR_SAVE); + histOp = 0; + histOff = insertOff; + } +} + +int historyBufferScroll(int n) { + if (IS_SET(MODE_ALTSCREEN) || !n) return histOp; + int p=abs(n=(n<0) ? max(n,-term.row) : min(n,term.row)), r=term.row-p, + s=sizeof(*term.dirty), *ptr=histOp?&histOff:&insertOff; + if (!histMode || histOp) tfulldirt(); else { + memmove(&term.dirty[-min(n,0)], &term.dirty[max(n,0)], s*r); + memset(&term.dirty[n>0 ? r : 0], 0, s * p); + } + term.line = &buf[*ptr = (buffSize+*ptr+n) % buffSize]; + // Cut part of selection removed from buffer, and update sel.ne/b. + int const prevOffBuf = sel.alt ? 0 : insertOff + term.row; + if (sel.ob.x != -1 && !histOp && n) { + int const offBuf = sel.alt ? 0 : insertOff + term.row, + pb = rangeY(sel.ob.y - prevOffBuf), + pe = rangeY(sel.oe.y - prevOffBuf); + int const b = rangeY(sel.ob.y - offBuf), nln = n < 0, + e = rangeY(sel.oe.y - offBuf), last = offBuf - nln; + if (pb != b && ((pb < b) != nln)) sel.ob.y = last; + if (pe != e && ((pe < e) != nln)) sel.oe.y = last; + if (sel.oe.y == last && sel.ob.y == last) selclear(); + } + selnormalize(); + // Clear the new region exposed by the shift. + if (!histOp) tclearregion(0, n>0?r+1:0, buffCols-1, n>0?term.row:p-1); + return 1; +} + +int historyMove(int x, int y, int ly) { + historyOpToggle(1, 1); + y += ((term.c.x += x) < 0 ?term.c.x-term.col :term.c.x) / term.col;//< x + if ((term.c.x %= term.col) < 0) term.c.x += term.col; + if ((term.c.y += y) >= term.row) ly += term.c.y - term.row + 1; //< y + else if (term.c.y < 0) ly += term.c.y; + term.c.y = MIN(MAX(term.c.y, 0), term.row - 1); + // Check if scroll is necessary / arrived at top / bottom of terminal history + int t = 0, b = 0, finTop = ly < 0, finBot = ly > 0; + if (!IS_SET(MODE_ALTSCREEN)) { + b=rangeY(insertOff-histOff), t=-rangeY(-term.row-(insertOff-histOff)); + finBot = ly > b, finTop=histMode&&((-ly>-t)); + } + if ((finTop || finBot) && (x||y)) term.c.x = finBot ? term.col-1 : 0; + historyBufferScroll(finBot ? b : (finTop ? t : ly)); + historyOpToggle(-1, 1); + return finTop || finBot; +} + +void selnormalize(void) { + historyOpToggle(1, 1); + + int const oldb = sel.nb.y, olde = sel.ne.y; + if (sel.ob.x == -1) { + sel.ne.y = sel.nb.y = -1; + } else { + int const offsetBuffer = sel.alt ? 0 : insertOff + term.row; + int const off = sel.alt ? 0 : (histMode ? histOff : insertOff); + int const nby = rangeY(sel.ob.y - off), + ney = rangeY(sel.oe.y - off); + sel.swap = rangeY(sel.ob.y - offsetBuffer) + > rangeY(sel.oe.y - offsetBuffer); + sel.nb.y = sel.swap ? ney : nby; + sel.ne.y = !sel.swap ? ney : nby; + int const cnb = sel.nb.y < term.row, cne = sel.ne.y < term.row; + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + if (cnb) sel.nb.x = (!sel.swap) ? sel.ob.x : sel.oe.x; + if (cne) sel.ne.x = (!sel.swap) ? sel.oe.x : sel.ob.x; + } else { + if (cnb) sel.nb.x = MIN(sel.ob.x, sel.oe.x); + if (cne) sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + } + int const nBet=sel.nb.y<=sel.ne.y, oBet=oldb<=olde; + for (int i = 0; i < term.row; ++i) { + int const n = nBet ? BETWEEN(i, sel.nb.y, sel.ne.y) + : OUT(i, sel.nb.y, sel.ne.y); + term.dirty[i] |= (sel.type == SEL_RECTANGULAR && n) || + (n != (oBet ? BETWEEN(i,oldb,olde) : OUT(i,oldb,olde))); + + } + if (BETWEEN(oldb, 0, term.row - 1)) term.dirty[oldb] = 1; + if (BETWEEN(olde, 0, term.row - 1)) term.dirty[olde] = 1; + if (BETWEEN(sel.nb.y, 0, term.row - 1)) term.dirty[sel.nb.y] = 1; + if (BETWEEN(sel.ne.y, 0, term.row - 1)) term.dirty[sel.ne.y] = 1; + + historyOpToggle(-1, 1); +} +#endif // VIM_BROWSE_PATCH + void selstart(int col, int row, int snap) { @@ -435,18 +575,27 @@ selstart(int col, int row, int snap) sel.alt = IS_SET(MODE_ALTSCREEN); sel.snap = snap; sel.oe.x = sel.ob.x = col; + #if VIM_BROWSE_PATCH + sel.oe.y = sel.ob.y = row + !sel.alt * (histMode ? histOff : insertOff); + if (sel.snap != 0) sel.mode = SEL_READY; + #else sel.oe.y = sel.ob.y = row; + #endif // VIM_BROWSE_PATCH selnormalize(); + #if !VIM_BROWSE_PATCH if (sel.snap != 0) sel.mode = SEL_READY; tsetdirt(sel.nb.y, sel.ne.y); + #endif // VIM_BROWSE_PATCH } void selextend(int col, int row, int type, int done) { + #if !VIM_BROWSE_PATCH int oldey, oldex, oldsby, oldsey, oldtype; + #endif // VIM_BROWSE_PATCH if (sel.mode == SEL_IDLE) return; @@ -455,23 +604,32 @@ selextend(int col, int row, int type, int done) return; } + #if !VIM_BROWSE_PATCH oldey = sel.oe.y; oldex = sel.oe.x; oldsby = sel.nb.y; oldsey = sel.ne.y; oldtype = sel.type; + #endif // VIM_BROWSE_PATCH sel.oe.x = col; + #if VIM_BROWSE_PATCH + sel.oe.y = row + (sel.alt ? 0 : (histMode ? histOff : insertOff)); + #else sel.oe.y = row; + #endif // VIM_BROWSE_PATCH selnormalize(); sel.type = type; + #if !VIM_BROWSE_PATCH if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + #endif // VIM_BROWSE_PATCH sel.mode = done ? SEL_IDLE : SEL_READY; } +#if !VIM_BROWSE_PATCH void selnormalize(void) { @@ -499,17 +657,7 @@ selnormalize(void) if (tlinelen(sel.ne.y) <= sel.ne.x) sel.ne.x = term.col - 1; } - -int -has_content_forward(int x, int y) -{ - int nx; - for (nx = x; nx < term.col; ++nx) - if (term.line[y][nx].u != '\n' && term.line[y][nx].u != ' ') - return 1; - return 0; -} - +#endif // VIM_BROWSE_PATCH int selected(int x, int y) @@ -517,17 +665,24 @@ selected(int x, int y) if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) return 0; - if (!has_content_forward(x, y)) - return 0; + if (sel.type == SEL_RECTANGULAR) return BETWEEN(y, sel.nb.y, sel.ne.y) && BETWEEN(x, sel.nb.x, sel.ne.x); + #if VIM_BROWSE_PATCH + return ((sel.nb.y > sel.ne.y) ? OUT(y, sel.nb.y, sel.ne.y) + : BETWEEN(y, sel.nb.y, sel.ne.y)) && + (y != sel.nb.y || x >= sel.nb.x) && + (y != sel.ne.y || x <= sel.ne.x); + #else return BETWEEN(y, sel.nb.y, sel.ne.y) && (y != sel.nb.y || x >= sel.nb.x) && (y != sel.ne.y || x <= sel.ne.x); + #endif // VIM_BROWSE_PATCH } +#if !VIM_BROWSE_PATCH void selsnap(int *x, int *y, int direction) { @@ -541,7 +696,11 @@ selsnap(int *x, int *y, int direction) * Snap around if the word wraps around at the end or * beginning of a line. */ + #if SCROLLBACK_PATCH prevgp = &TLINE(*y)[*x]; + #else + prevgp = &term.line[*y][*x]; + #endif // SCROLLBACK_PATCH prevdelim = ISDELIM(prevgp->u); for (;;) { newx = *x + direction; @@ -556,14 +715,22 @@ selsnap(int *x, int *y, int direction) yt = *y, xt = *x; else yt = newy, xt = newx; + #if SCROLLBACK_PATCH if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + #else + if (!(term.line[yt][xt].mode & ATTR_WRAP)) + #endif // SCROLLBACK_PATCH break; } if (newx >= tlinelen(newy)) break; + #if SCROLLBACK_PATCH gp = &TLINE(newy)[newx]; + #else + gp = &term.line[newy][newx]; + #endif // SCROLLBACK_PATCH delim = ISDELIM(gp->u); if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || (delim && gp->u != prevgp->u))) @@ -584,15 +751,23 @@ selsnap(int *x, int *y, int direction) *x = (direction < 0) ? 0 : term.col - 1; if (direction < 0) { for (; *y > 0; *y += direction) { - if (!(TLINE(*y-1)[term.col-1].mode - & ATTR_WRAP)) { + #if SCROLLBACK_PATCH + if (!(TLINE(*y-1)[term.col-1].mode & ATTR_WRAP)) + #else + if (!(term.line[*y-1][term.col-1].mode & ATTR_WRAP)) + #endif // SCROLLBACK_PATCH + { break; } } } else if (direction > 0) { for (; *y < term.row-1; *y += direction) { - if (!(TLINE(*y)[term.col-1].mode - & ATTR_WRAP)) { + #if SCROLLBACK_PATCH + if (!(TLINE(*y)[term.col-1].mode & ATTR_WRAP)) + #else + if (!(term.line[*y][term.col-1].mode & ATTR_WRAP)) + #endif // SCROLLBACK_PATCH + { break; } } @@ -600,37 +775,87 @@ selsnap(int *x, int *y, int direction) break; } } +#endif // VIM_BROWSE_PATCH char * getsel(void) { char *str, *ptr; + #if VIM_BROWSE_PATCH + int y, yy, bufsize, lastx; + #else int y, bufsize, lastx, linelen; + #endif // VIM_BROWSE_PATCH const Glyph *gp, *last; if (sel.ob.x == -1) return NULL; + #if VIM_BROWSE_PATCH + int const start = sel.swap ? sel.oe.y : sel.ob.y, h = rows(); + int endy = (sel.swap ? sel.ob.y : sel.oe.y); + for (; endy < start; endy += h); + Line * const cbuf = IS_SET(MODE_ALTSCREEN) ? term.line : buf; + bufsize = (term.col+1) * (endy-start+1 ) * UTF_SIZ; + assert(bufsize > 0); + #else bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + #endif // VIM_BROWSE_PATCH ptr = str = xmalloc(bufsize); /* append every set & selected glyph to the selection */ - for (y = sel.nb.y; y <= sel.ne.y; y++) { + #if VIM_BROWSE_PATCH + for (y = start; y <= endy; y++) + #else + for (y = sel.nb.y; y <= sel.ne.y; y++) + #endif // VIM_BROWSE_PATCH + { + #if VIM_BROWSE_PATCH + yy = y % h; + #else if ((linelen = tlinelen(y)) == 0) { *ptr++ = '\n'; continue; } + #endif // VIM_BROWSE_PATCH if (sel.type == SEL_RECTANGULAR) { + #if VIM_BROWSE_PATCH + gp = &cbuf[yy][sel.nb.x]; + #elif SCROLLBACK_PATCH gp = &TLINE(y)[sel.nb.x]; + #else + gp = &term.line[y][sel.nb.x]; + #endif // SCROLLBACK_PATCH lastx = sel.ne.x; } else { + #if VIM_BROWSE_PATCH + gp = &cbuf[yy][start == y ? sel.nb.x : 0]; + #elif SCROLLBACK_PATCH gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + #else + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + #endif // SCROLLBACK_PATCH + #if VIM_BROWSE_PATCH + lastx = (endy == y) ? sel.ne.x : term.col-1; + #else lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + #endif // VIM_BROWSE_PATCH } + #if VIM_BROWSE_PATCH + last = &cbuf[yy][lastx]; + #elif SCROLLBACK_PATCH last = &TLINE(y)[MIN(lastx, linelen-1)]; + #else + last = &term.line[y][MIN(lastx, linelen-1)]; + #endif // SCROLLBACK_PATCH + #if VIM_BROWSE_PATCH + if (!(cbuf[yy][term.col - 1].mode & ATTR_WRAP)) + while (last > gp && last->u == ' ') --last; + #else while (last >= gp && last->u == ' ') --last; + #endif // VIM_BROWSE_PATCH for ( ; gp <= last; ++gp) { if (gp->mode & ATTR_WDUMMY) @@ -648,8 +873,13 @@ getsel(void) * st. * FIXME: Fix the computer world. */ - if ((y < sel.ne.y || lastx >= linelen) && - (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + if ( + #if VIM_BROWSE_PATCH + (y < endy || lastx == term.col - 1) + #else + (y < sel.ne.y || lastx >= linelen) + #endif // + && (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } *ptr = 0; @@ -663,7 +893,11 @@ selclear(void) return; sel.mode = SEL_IDLE; sel.ob.x = -1; + #if VIM_BROWSE_PATCH + selnormalize(); + #else tsetdirt(sel.nb.y, sel.ne.y); + #endif // VIM_BROWSE_PATCH } void @@ -717,6 +951,7 @@ execsh(char *cmd, char **args) setenv("SHELL", sh, 1); setenv("HOME", pw->pw_dir, 1); setenv("TERM", termname, 1); + setenv("COLORTERM", "truecolor", 1); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); @@ -735,11 +970,34 @@ sigchld(int a) int stat; pid_t p; + #if EXTERNALPIPEIN_PATCH && EXTERNALPIPE_PATCH + if ((p = waitpid((extpipeactive ? -1 : pid), &stat, WNOHANG)) < 0) + #else if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + #endif // EXTERNALPIPEIN_PATCH die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + #if EXTERNALPIPE_PATCH + if (pid != p) { + if (!extpipeactive) + return; + + if (p == 0 && wait(&stat) < 0) + die("wait: %s\n", strerror(errno)); + + /* reinstall sigchld handler */ + signal(SIGCHLD, sigchld); + extpipeactive = 0; + return; + } + #else if (pid != p) return; + #endif // EXTERNALPIPE_PATCH + + #if EXTERNALPIPEIN_PATCH && EXTERNALPIPE_PATCH + close(csdfd); + #endif // EXTERNALPIPEIN_PATCH if (WIFEXITED(stat) && WEXITSTATUS(stat)) die("child exited with status %d\n", WEXITSTATUS(stat)); @@ -776,6 +1034,9 @@ int ttynew(const char *line, char *cmd, const char *out, char **args) { int m, s; + #if EXTERNALPIPEIN_PATCH && EXTERNALPIPE_PATCH + struct sigaction sa; + #endif // EXTERNALPIPEIN_PATCH if (out) { term.mode |= MODE_PRINT; @@ -814,7 +1075,7 @@ ttynew(const char *line, char *cmd, const char *out, char **args) if (ioctl(s, TIOCSCTTY, NULL) < 0) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); if (s > 2) - close(s); + close(s); #ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); @@ -823,12 +1084,25 @@ ttynew(const char *line, char *cmd, const char *out, char **args) break; default: #ifdef __OpenBSD__ + #if RIGHTCLICKTOPLUMB_PATCH || OPENCOPIED_PATCH + if (pledge("stdio rpath tty proc ps exec", NULL) == -1) + #else if (pledge("stdio rpath tty proc", NULL) == -1) + #endif // RIGHTCLICKTOPLUMB_PATCH die("pledge\n"); #endif + #if EXTERNALPIPEIN_PATCH && EXTERNALPIPE_PATCH + csdfd = s; + cmdfd = m; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigchld; + sigaction(SIGCHLD, &sa, NULL); + #else close(s); cmdfd = m; signal(SIGCHLD, sigchld); + #endif // EXTERNALPIPEIN_PATCH break; } return cmdfd; @@ -842,7 +1116,11 @@ ttyread(void) int ret, written; /* append read bytes to unprocessed bytes */ + #if SYNC_PATCH + ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); + #else ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + #endif // SYNC_PATCH switch (ret) { case 0: @@ -850,7 +1128,11 @@ ttyread(void) case -1: die("couldn't read from shell: %s\n", strerror(errno)); default: + #if SYNC_PATCH + buflen += twrite_aborted ? 0 : ret; + #else buflen += ret; + #endif // SYNC_PATCH written = twrite(buf, buflen, 0); buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ @@ -864,9 +1146,11 @@ void ttywrite(const char *s, size_t n, int may_echo) { const char *next; + #if SCROLLBACK_PATCH Arg arg = (Arg) { .i = term.scr }; kscrolldown(&arg); + #endif // SCROLLBACK_PATCH if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); @@ -983,6 +1267,12 @@ tattrset(int attr) return 0; } +int +tisaltscr(void) +{ + return IS_SET(MODE_ALTSCREEN); +} + void tsetdirt(int top, int bot) { @@ -1013,14 +1303,21 @@ tsetdirtattr(int attr) void tfulldirt(void) { + #if SYNC_PATCH + tsync_end(); + #endif // SYNC_PATCH tsetdirt(0, term.row-1); } void tcursor(int mode) { + #if VIM_BROWSE_PATCH + int alt = (histOp) ? 0 : (IS_SET(MODE_ALTSCREEN) + 1); + #else static TCursor c[2]; int alt = IS_SET(MODE_ALTSCREEN); + #endif // VIM_BROWSE_PATCH if (mode == CURSOR_SAVE) { c[alt] = term.c; @@ -1034,6 +1331,9 @@ void treset(void) { uint i; + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH term.c = (TCursor){{ .mode = ATTR_NULL, @@ -1053,9 +1353,17 @@ treset(void) for (i = 0; i < 2; i++) { tmoveto(0, 0); tcursor(CURSOR_SAVE); + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + tclearregion(0, 0, term.maxcol-1, term.row-1); + #else tclearregion(0, 0, term.col-1, term.row-1); + #endif // COLUMNS_PATCH tswapscreen(); } + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) + im->should_delete = 1; + #endif // SIXEL_PATCH } void @@ -1070,63 +1378,42 @@ void tswapscreen(void) { Line *tmp = term.line; + #if SIXEL_PATCH + ImageList *im = term.images; + #endif // SIXEL_PATCH term.line = term.alt; term.alt = tmp; + #if SIXEL_PATCH + term.images = term.images_alt; + term.images_alt = im; + #endif // SIXEL_PATCH term.mode ^= MODE_ALTSCREEN; tfulldirt(); } void -kscrolldown(const Arg* a) +tscrolldown(int orig, int n) { - int n = a->i; + #if OPENURLONCLICK_PATCH + restoremousecursor(); + #endif //OPENURLONCLICK_PATCH - if (n < 0) - n = term.row + n; - - if (n > term.scr) - n = term.scr; - - if (term.scr > 0) { - term.scr -= n; - selscroll(0, -n); - tfulldirt(); - } -} - -void -kscrollup(const Arg* a) -{ - int n = a->i; - - if (n < 0) - n = term.row + n; - - if (term.scr <= HISTSIZE-n) { - term.scr += n; - selscroll(0, n); - tfulldirt(); - } -} - -void -tscrolldown(int orig, int n, int copyhist) -{ + #if VIM_BROWSE_PATCH + if (!orig && historyBufferScroll(-n)) + return; + #endif // VIM_BROWSE_PATCH int i; Line temp; LIMIT(n, 0, term.bot-orig+1); - if (copyhist) { - term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; - temp = term.hist[term.histi]; - term.hist[term.histi] = term.line[term.bot]; - term.line[term.bot] = temp; - } - tsetdirt(orig, term.bot-n); + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + tclearregion(0, term.bot-n+1, term.maxcol-1, term.bot); + #else tclearregion(0, term.bot-n+1, term.col-1, term.bot); + #endif // COLUMNS_PATCH for (i = term.bot; i >= orig+n; i--) { temp = term.line[i]; @@ -1134,29 +1421,65 @@ tscrolldown(int orig, int n, int copyhist) term.line[i-n] = temp; } + #if SIXEL_PATCH + /* move images, if they are inside the scrolling region */ + ImageList *im; + for (im = term.images; im; im = im->next) { + if (im->y * win.ch + im->height > orig * win.ch && im->y <= term.bot) { + im->y += n; + im->should_delete |= (im->y >= term.row); + } + } + #endif // SIXEL_PATCH + + #if SCROLLBACK_PATCH if (term.scr == 0) selscroll(orig, n); + #else + selscroll(orig, n); + #endif // SCROLLBACK_PATCH } void +#if SCROLLBACK_PATCH tscrollup(int orig, int n, int copyhist) +#else +tscrollup(int orig, int n) +#endif // SCROLLBACK_PATCH { + #if OPENURLONCLICK_PATCH + restoremousecursor(); + #endif //OPENURLONCLICK_PATCH + + #if VIM_BROWSE_PATCH + if (!orig && historyBufferScroll(n)) + return; + #endif // VIM_BROWSE_PATCH int i; Line temp; LIMIT(n, 0, term.bot-orig+1); - if (copyhist) { - term.histi = (term.histi + 1) % HISTSIZE; - temp = term.hist[term.histi]; - term.hist[term.histi] = term.line[orig]; - term.line[orig] = temp; + #if SCROLLBACK_PATCH + if (copyhist && !IS_SET(MODE_ALTSCREEN)) { + for (i = 0; i < n; i++) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig+i]; + term.line[orig+i] = temp; + } + term.histn = MIN(term.histn + n, HISTSIZE); + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); } + #endif // SCROLLBACK_PATCH - if (term.scr > 0 && term.scr < HISTSIZE) - term.scr = MIN(term.scr + n, HISTSIZE-1); - + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + tclearregion(0, orig, term.maxcol-1, orig+n-1); + #else tclearregion(0, orig, term.col-1, orig+n-1); + #endif // COLUMNS_PATCH tsetdirt(orig+n, term.bot); for (i = orig; i <= term.bot-n; i++) { @@ -1165,14 +1488,27 @@ tscrollup(int orig, int n, int copyhist) term.line[i+n] = temp; } + #if SIXEL_PATCH + #if SCROLLBACK_PATCH + if (term.scr == 0) + scroll_images(-1 * n); + #else + scroll_images(-1 * n); + #endif + #endif // SIXEL_PATCH + + #if SCROLLBACK_PATCH if (term.scr == 0) selscroll(orig, -n); + #else + selscroll(orig, -n); + #endif // SCROLLBACK_PATCH } void selscroll(int orig, int n) { - if (sel.ob.x == -1) + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) return; if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { @@ -1195,13 +1531,41 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { + #if SCROLLBACK_PATCH tscrollup(term.top, 1, 1); + #else + tscrollup(term.top, 1); + #endif // SCROLLBACK_PATCH } else { y++; } tmoveto(first_col ? 0 : term.c.x, y); } +#if UNDERCURL_PATCH +void +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) +{ + int i = 0; + for (; i < CAR_PER_ARG; i++) + params[cursor][i] = -1; + + if (**p != ':') + return; + + char *np = NULL; + i = 0; + + while (**p == ':' && i < CAR_PER_ARG) { + while (**p == ':') + (*p)++; + params[cursor][i] = strtol(*p, &np, 10); + *p = np; + i++; + } +} +#endif // UNDERCURL_PATCH + void csiparse(void) { @@ -1224,6 +1588,9 @@ csiparse(void) v = -1; csiescseq.arg[csiescseq.narg++] = v; p = np; + #if UNDERCURL_PATCH + readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); + #endif // UNDERCURL_PATCH if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) break; p++; @@ -1291,8 +1658,10 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) term.line[y][x] = *attr; term.line[y][x].u = u; + #if BOXDRAW_PATCH if (isboxdraw(u)) term.line[y][x].mode |= ATTR_BOXDRAW; + #endif // BOXDRAW_PATCH } void @@ -1306,8 +1675,16 @@ tclearregion(int x1, int y1, int x2, int y2) if (y1 > y2) temp = y1, y1 = y2, y2 = temp; + #if VIM_BROWSE_PATCH + LIMIT(x1, 0, buffCols-1); + LIMIT(x2, 0, buffCols-1); + #elif COLUMNS_PATCH LIMIT(x1, 0, term.maxcol-1); LIMIT(x2, 0, term.maxcol-1); + #else + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + #endif // VIM_BROWSE_PATCH LIMIT(y1, 0, term.row-1); LIMIT(y2, 0, term.row-1); @@ -1363,14 +1740,18 @@ void tinsertblankline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n, 0); + tscrolldown(term.c.y, n); } void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) + #if SCROLLBACK_PATCH tscrollup(term.c.y, n, 0); + #else + tscrollup(term.c.y, n); + #endif // SCROLLBACK_PATCH } int32_t @@ -1443,6 +1824,12 @@ tsetattr(const int *attr, int l) ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; + #if UNDERCURL_PATCH + term.c.attr.ustyle = -1; + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + #endif // UNDERCURL_PATCH break; case 1: term.c.attr.mode |= ATTR_BOLD; @@ -1454,7 +1841,18 @@ tsetattr(const int *attr, int l) term.c.attr.mode |= ATTR_ITALIC; break; case 4: + #if UNDERCURL_PATCH + term.c.attr.ustyle = csiescseq.carg[i][0]; + + if (term.c.attr.ustyle != 0) + term.c.attr.mode |= ATTR_UNDERLINE; + else + term.c.attr.mode &= ~ATTR_UNDERLINE; + + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + #else term.c.attr.mode |= ATTR_UNDERLINE; + #endif // UNDERCURL_PATCH break; case 5: /* slow blink */ /* FALLTHROUGH */ @@ -1493,27 +1891,65 @@ tsetattr(const int *attr, int l) break; case 38: if ((idx = tdefcolor(attr, &i, l)) >= 0) + #if MONOCHROME_PATCH + term.c.attr.fg = defaultfg; + #else term.c.attr.fg = idx; + #endif // MONOCHROME_PATCH break; case 39: term.c.attr.fg = defaultfg; break; case 48: if ((idx = tdefcolor(attr, &i, l)) >= 0) + #if MONOCHROME_PATCH + term.c.attr.bg = 0; + #else term.c.attr.bg = idx; + #endif // MONOCHROME_PATCH break; case 49: term.c.attr.bg = defaultbg; break; + #if UNDERCURL_PATCH + case 58: + term.c.attr.ucolor[0] = csiescseq.carg[i][1]; + term.c.attr.ucolor[1] = csiescseq.carg[i][2]; + term.c.attr.ucolor[2] = csiescseq.carg[i][3]; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + case 59: + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + #endif // UNDERCURL_PATCH default: if (BETWEEN(attr[i], 30, 37)) { + #if MONOCHROME_PATCH + term.c.attr.fg = defaultfg; + #else term.c.attr.fg = attr[i] - 30; + #endif // MONOCHROME_PATCH } else if (BETWEEN(attr[i], 40, 47)) { + #if MONOCHROME_PATCH + term.c.attr.bg = 0; + #else term.c.attr.bg = attr[i] - 40; + #endif // MONOCHROME_PATCH } else if (BETWEEN(attr[i], 90, 97)) { + #if MONOCHROME_PATCH + term.c.attr.fg = defaultfg; + #else term.c.attr.fg = attr[i] - 90 + 8; + #endif // MONOCHROME_PATCH } else if (BETWEEN(attr[i], 100, 107)) { + #if MONOCHROME_PATCH + term.c.attr.bg = 0; + #else term.c.attr.bg = attr[i] - 100 + 8; + #endif // MONOCHROME_PATCH } else { fprintf(stderr, "erresc(default): gfx attr %d unknown\n", @@ -1544,7 +1980,8 @@ tsetscroll(int t, int b) void tsetmode(int priv, int set, const int *args, int narg) { - int alt; const int *lim; + int alt; + const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1615,8 +2052,11 @@ tsetmode(int priv, int set, const int *args, int narg) break; alt = IS_SET(MODE_ALTSCREEN); if (alt) { - tclearregion(0, 0, term.col-1, - term.row-1); + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + tclearregion(0, 0, term.maxcol-1, term.row-1); + #else + tclearregion(0, 0, term.col-1, term.row-1); + #endif // COLUMNS_PATCH } if (set ^ alt) /* set is always 1 or 0 */ tswapscreen(); @@ -1674,8 +2114,16 @@ tsetmode(int priv, int set, const int *args, int narg) void csihandle(void) { - char buf[40]; + char buffer[40]; int len; + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + int maxcol = term.maxcol; + #else + int maxcol = term.col; + #endif // COLUMNS_PATCH switch (csiescseq.mode[0]) { default: @@ -1773,20 +2221,88 @@ csihandle(void) case 'J': /* ED -- Clear screen */ switch (csiescseq.arg[0]) { case 0: /* below */ - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); if (term.c.y < term.row-1) { - tclearregion(0, term.c.y+1, term.col-1, + tclearregion(0, term.c.y+1, maxcol-1, term.row-1); } break; case 1: /* above */ if (term.c.y > 1) - tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, 0, maxcol-1, term.c.y-1); tclearregion(0, term.c.y, term.c.x, term.c.y); break; - case 2: /* all */ - tclearregion(0, 0, term.col-1, term.row-1); + case 2: /* screen */ + #if SCROLLBACK_PATCH || VIM_BROWSE_PATCH + if (!IS_SET(MODE_ALTSCREEN)) { + #if SCROLLBACK_PATCH + kscrolldown(&((Arg){ .i = term.scr })); + #endif + int n, m, bot = term.bot; + term.bot = term.row-1; + for (n = term.row-1; n >= 0; n--) { + for (m = 0; m < maxcol && term.line[n][m].u == ' ' && !term.line[n][m].mode; m++); + if (m < maxcol) { + #if SCROLLBACK_PATCH + tscrollup(0, n+1, 1); + #else + tscrollup(0, n+1); + #endif + break; + } + } + if (n < term.row-1) + tclearregion(0, 0, maxcol-1, term.row-n-2); + term.bot = bot; + break; + } + #endif // SCROLLBACK_PATCH + + tclearregion(0, 0, maxcol-1, term.row-1); + + #if SIXEL_PATCH + for (im = term.images; im; im = im->next) + im->should_delete = 1; + #endif // SIXEL_PATCH break; + case 3: /* scrollback */ + #if VIM_BROWSE_PATCH + if (!IS_SET(MODE_ALTSCREEN)) { + Glyph g=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0}; + for (int i = 0; i < buffSize; ++i) { + if (!BETWEEN(i, insertOff, insertOff + term.row - 1) && + !(insertOff + term.row > buffSize && + BETWEEN(i, 0, (insertOff + term.row - 1) % buffSize))) { + for (int j = 0; j < term.col; ++j) + buf[i][j] = g; + } + } + } + #elif SCROLLBACK_PATCH + if (!IS_SET(MODE_ALTSCREEN)) { + term.scr = 0; + term.histi = 0; + term.histn = 0; + Glyph g=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0}; + for (int i = 0; i < HISTSIZE; i++) { + for (int j = 0; j < maxcol; j++) + term.hist[i][j] = g; + } + } + #endif // SCROLLBACK_PATCH + #if SIXEL_PATCH + if (!IS_SET(MODE_ALTSCREEN)) { + for (im = term.images; im; im = im->next) + im->should_delete |= (im->y * win.ch + im->height <= 0); + } + #endif // SIXEL_PATCH + break; + #if SIXEL_PATCH + case 6: /* sixels */ + for (im = term.images; im; im = im->next) + im->should_delete = 1; + break; + #endif // SIXEL_PATCH default: goto unknown; } @@ -1794,24 +2310,30 @@ csihandle(void) case 'K': /* EL -- Clear line */ switch (csiescseq.arg[0]) { case 0: /* right */ - tclearregion(term.c.x, term.c.y, term.col-1, + tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); break; case 1: /* left */ tclearregion(0, term.c.y, term.c.x, term.c.y); break; case 2: /* all */ - tclearregion(0, term.c.y, term.col-1, term.c.y); + tclearregion(0, term.c.y, maxcol-1, term.c.y); break; } break; case 'S': /* SU -- Scroll line up */ DEFAULT(csiescseq.arg[0], 1); + #if SIXEL_PATCH && SCROLLBACK_PATCH + tscrollup(term.top, csiescseq.arg[0], 1); + #elif SCROLLBACK_PATCH tscrollup(term.top, csiescseq.arg[0], 0); + #else + tscrollup(term.top, csiescseq.arg[0]); + #endif // SCROLLBACK_PATCH break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0], 0); + tscrolldown(term.top, csiescseq.arg[0]); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -1819,6 +2341,12 @@ csihandle(void) break; case 'l': /* RM -- Reset Mode */ tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + #if SIXEL_PATCH + if (IS_SET(MODE_ALTSCREEN)) { + for (im = term.images; im; im = im->next) + im->should_delete = 1; + } + #endif // SIXEL_PATCH break; case 'M': /* DL -- Delete lines */ DEFAULT(csiescseq.arg[0], 1); @@ -1847,11 +2375,18 @@ csihandle(void) case 'm': /* SGR -- Terminal attribute (color) */ tsetattr(csiescseq.arg, csiescseq.narg); break; - case 'n': /* DSR – Device Status Report (cursor position) */ - if (csiescseq.arg[0] == 6) { - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", - term.c.y+1, term.c.x+1); - ttywrite(buf, len, 0); + case 'n': /* DSR -- Device Status Report */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) ";R" */ + len = snprintf(buffer, sizeof(buffer), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buffer, len, 0); + break; + default: + goto unknown; } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -1867,6 +2402,36 @@ csihandle(void) case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ tcursor(CURSOR_SAVE); break; + #if CSI_22_23_PATCH + case 't': /* title stack operations */ + switch (csiescseq.arg[0]) { + case 22: /* pust current title on stack */ + switch (csiescseq.arg[1]) { + case 0: + case 1: + case 2: + xpushtitle(); + break; + default: + goto unknown; + } + break; + case 23: /* pop last title from stack */ + switch (csiescseq.arg[1]) { + case 0: + case 1: + case 2: + xsettitle(NULL, 1); + break; + default: + goto unknown; + } + break; + default: + goto unknown; + } + break; + #endif // CSI_22_23_PATCH case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ tcursor(CURSOR_LOAD); break; @@ -1914,28 +2479,39 @@ csireset(void) } void -osc_color_response(int num, int index, int is_osc4) +osc4_color_response(int num) { int n; char buf[32]; unsigned char r, g, b; - if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { - fprintf(stderr, "erresc: failed to fetch %s color %d\n", - is_osc4 ? "osc4" : "osc", - is_osc4 ? num : index); + if (xgetcolor(num, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); return; } - n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - is_osc4 ? "4;" : "", num, r, r, g, g, b, b); - if (n < 0 || n >= sizeof(buf)) { - fprintf(stderr, "error: %s while printing %s response\n", - n < 0 ? "snprintf failed" : "truncation occurred", - is_osc4 ? "osc4" : "osc"); - } else { - ttywrite(buf, n, 1); + n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +osc_color_response(int index, int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); + return; } + + n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); } void @@ -1943,11 +2519,10 @@ strhandle(void) { char *p = NULL, *dec; int j, narg, par; - const struct { int idx; char *str; } osc_table[] = { - { defaultfg, "foreground" }, - { defaultbg, "background" }, - { defaultcs, "cursor" } - }; + #if SIXEL_PATCH + ImageList *new_image; + int i; + #endif // SIXEL_PATCH term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -1958,7 +2533,11 @@ strhandle(void) switch (par) { case 0: if (narg > 1) { + #if CSI_22_23_PATCH + xsettitle(strescseq.args[1], 0); + #else xsettitle(strescseq.args[1]); + #endif // CSI_22_23_PATCH xseticontitle(strescseq.args[1]); } return; @@ -1968,7 +2547,11 @@ strhandle(void) return; case 2: if (narg > 1) + #if CSI_22_23_PATCH + xsettitle(strescseq.args[1], 0); + #else xsettitle(strescseq.args[1]); + #endif // CSI_22_23_PATCH return; case 52: if (narg > 2 && allowwindowops) { @@ -1981,53 +2564,131 @@ strhandle(void) } } return; + case 8: /* Clear Hyperlinks */ + return; case 10: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultfg, 10); + else if (xsetcolorname(defaultfg, p)) + fprintf(stderr, "erresc: invalid foreground color: %s\n", p); + else + tfulldirt(); + return; case 11: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultbg, 11); + else if (xsetcolorname(defaultbg, p)) + fprintf(stderr, "erresc: invalid background color: %s\n", p); + else + tfulldirt(); + return; case 12: if (narg < 2) break; - p = strescseq.args[1]; - if ((j = par - 10) < 0 || j >= LEN(osc_table)) - break; /* shouldn't be possible */ - if (!strcmp(p, "?")) { - osc_color_response(par, osc_table[j].idx, 0); - } else if (xsetcolorname(osc_table[j].idx, p)) { - fprintf(stderr, "erresc: invalid %s color: %s\n", - osc_table[j].str, p); - } else { + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else tfulldirt(); - } return; case 4: /* color set */ - if (narg < 3) + if ((par == 4 && narg < 3) || narg < 2) break; - p = strescseq.args[2]; + p = strescseq.args[((par == 4) ? 2 : 1)]; /* FALLTHROUGH */ case 104: /* color reset */ - j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + if (par == 10) + j = defaultfg; + else if (par == 11) + j = defaultbg; + else if (par == 12) + j = defaultcs; + else + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (p && !strcmp(p, "?")) { - osc_color_response(j, 0, 1); - } else if (xsetcolorname(j, p)) { - if (par == 104 && narg <= 1) + if (p && !strcmp(p, "?")) + osc4_color_response(j); + else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) { + xloadcols(); return; /* color reset without parameter */ + } fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", j, p ? p : "(null)"); } else { - /* - * TODO if defaultbg color is changed, borders - * are dirty - */ + if (j == defaultbg) + xclearwin(); tfulldirt(); } return; } break; case 'k': /* old title set compatibility */ + #if CSI_22_23_PATCH + xsettitle(strescseq.args[0], 0); + #else xsettitle(strescseq.args[0]); + #endif // CSI_22_23_PATCH return; case 'P': /* DCS -- Device Control String */ + #if SIXEL_PATCH + if (IS_SET(MODE_SIXEL)) { + term.mode &= ~MODE_SIXEL; + new_image = malloc(sizeof(ImageList)); + memset(new_image, 0, sizeof(ImageList)); + new_image->x = term.c.x; + new_image->y = term.c.y; + new_image->width = sixel_st.image.width; + new_image->height = sixel_st.image.height; + new_image->pixels = malloc(new_image->width * new_image->height * 4); + if (sixel_parser_finalize(&sixel_st, new_image->pixels) != 0) { + perror("sixel_parser_finalize() failed"); + sixel_parser_deinit(&sixel_st); + return; + } + sixel_parser_deinit(&sixel_st); + if (term.images) { + ImageList *im; + for (im = term.images; im->next;) + im = im->next; + im->next = new_image; + new_image->prev = im; + } else { + term.images = new_image; + } + for (i = 0; i < (sixel_st.image.height + win.ch-1)/win.ch; ++i) { + int x; + tclearregion(term.c.x, term.c.y, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw-1, term.c.y); + for (x = term.c.x; x < MIN(term.col, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw); x++) + term.line[term.c.y][x].mode |= ATTR_SIXEL; + tnewline(0); + } + } + #endif // SIXEL_PATCH + #if SYNC_PATCH + /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ + if (strstr(strescseq.buf, "=1s") == strescseq.buf) + tsync_begin(); /* BSU */ + else if (strstr(strescseq.buf, "=2s") == strescseq.buf) + tsync_end(); /* ESU */ + #endif // SYNC_PATCH + #if SIXEL_PATCH || SYNC_PATCH + return; + #endif // SIXEL_PATCH | SYNC_PATCH case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2221,9 +2882,16 @@ tdectest(char c) void tstrsequence(uchar c) { + #if SIXEL_PATCH + strreset(); + #endif // SIXEL_PATCH + switch (c) { case 0x90: /* DCS -- Device Control String */ c = 'P'; + #if SIXEL_PATCH + term.esc |= ESC_DCS; + #endif // SIXEL_PATCH break; case 0x9f: /* APC -- Application Program Command */ c = '_'; @@ -2235,7 +2903,9 @@ tstrsequence(uchar c) c = ']'; break; } + #if !SIXEL_PATCH strreset(); + #endif // SIXEL_PATCH strescseq.type = c; term.esc |= ESC_STR; } @@ -2355,6 +3025,9 @@ eschandle(uchar ascii) term.esc |= ESC_UTF8; return 0; case 'P': /* DCS -- Device Control String */ + #if SIXEL_PATCH + term.esc |= ESC_DCS; + #endif // SIXEL_PATCH case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ case ']': /* OSC -- Operating System Command */ @@ -2374,7 +3047,11 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { + #if SCROLLBACK_PATCH tscrollup(term.top, 1, 1); + #else + tscrollup(term.top, 1); + #endif // SCROLLBACK_PATCH } else { tmoveto(term.c.x, term.c.y+1); } @@ -2387,7 +3064,7 @@ eschandle(uchar ascii) break; case 'M': /* RI -- Reverse index */ if (term.c.y == term.top) { - tscrolldown(term.top, 1, 1); + tscrolldown(term.top, 1); } else { tmoveto(term.c.x, term.c.y-1); } @@ -2397,8 +3074,19 @@ eschandle(uchar ascii) break; case 'c': /* RIS -- Reset to initial state */ treset(); + #if CSI_22_23_PATCH + xfreetitlestack(); + #endif // CSI_22_23_PATCH resettitle(); xloadcols(); + xsetmode(0, MODE_HIDE); + #if SCROLLBACK_PATCH + if (!IS_SET(MODE_ALTSCREEN)) { + term.scr = 0; + term.histi = 0; + term.histn = 0; + } + #endif // SCROLLBACK_PATCH break; case '=': /* DECPAM -- Application keypad */ xsetmode(1, MODE_APPKEYPAD); @@ -2433,7 +3121,12 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (u < 127 || !IS_SET(MODE_UTF8)) { + #if SIXEL_PATCH + if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) + #else + if (u < 127 || !IS_SET(MODE_UTF8)) + #endif // SIXEL_PATCH + { c[0] = u; width = len = 1; } else { @@ -2454,11 +3147,25 @@ tputc(Rune u) if (term.esc & ESC_STR) { if (u == '\a' || u == 030 || u == 032 || u == 033 || ISCONTROLC1(u)) { + #if SIXEL_PATCH + term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); + #else term.esc &= ~(ESC_START|ESC_STR); + #endif // SIXEL_PATCH term.esc |= ESC_STR_END; goto check_control_code; } + #if SIXEL_PATCH + if (IS_SET(MODE_SIXEL)) { + if (sixel_parser_parse(&sixel_st, (unsigned char *)&u, 1) != 0) + perror("sixel_parser_parse() failed"); + return; + } + if (term.esc & ESC_DCS) + goto check_control_code; + #endif // SIXEL_PATCH + if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends @@ -2491,6 +3198,9 @@ check_control_code: * they must not cause conflicts with sequences. */ if (control) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; tcontrolcode(u); /* * control codes are not shown ever @@ -2509,6 +3219,17 @@ check_control_code: csihandle(); } return; + #if SIXEL_PATCH + } else if (term.esc & ESC_DCS) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + csiparse(); + dcshandle(); + } + return; + #endif // SIXEL_PATCH } else if (term.esc & ESC_UTF8) { tdefutf8(u); } else if (term.esc & ESC_ALTCHARSET) { @@ -2527,8 +3248,10 @@ check_control_code: */ return; } + #if !VIM_BROWSE_PATCH if (selected(term.c.x, term.c.y)) selclear(); + #endif // VIM_BROWSE_PATCH gp = &term.line[term.c.y][term.c.x]; if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { @@ -2537,11 +3260,16 @@ check_control_code: gp = &term.line[term.c.y][term.c.x]; } - if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } if (term.c.x+width > term.col) { - tnewline(1); + if (IS_SET(MODE_WRAP)) + tnewline(1); + else + tmoveto(term.col - width, term.c.y); gp = &term.line[term.c.y][term.c.x]; } @@ -2573,8 +3301,18 @@ twrite(const char *buf, int buflen, int show_ctrl) Rune u; int n; + #if SYNC_PATCH + int su0 = su; + twrite_aborted = 0; + #endif // SYNC_PATCH + for (n = 0; n < buflen; n += charsize) { - if (IS_SET(MODE_UTF8)) { + #if SIXEL_PATCH + if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) + #else + if (IS_SET(MODE_UTF8)) + #endif // SIXEL_PATCH + { /* process a complete utf8 char */ charsize = utf8decode(buf + n, &u, buflen - n); if (charsize == 0) @@ -2583,6 +3321,12 @@ twrite(const char *buf, int buflen, int show_ctrl) u = buf[n] & 0xFF; charsize = 1; } + #if SYNC_PATCH + if (su0 && !su) { + twrite_aborted = 1; + break; // ESU - allow rendering before a new BSU + } + #endif // SYNC_PATCH if (show_ctrl && ISCONTROL(u)) { if (u & 0x80) { u &= 0x7f; @@ -2601,81 +3345,156 @@ twrite(const char *buf, int buflen, int show_ctrl) void tresize(int col, int row) { - int i, j; - int *bp; - int tmp; + int i; + #if SCROLLBACK_PATCH + int j; + #endif // SCROLLBACK_PATCH + #if VIM_BROWSE_PATCH + int const colSet = col, alt = IS_SET(MODE_ALTSCREEN), ini = buf == NULL; + col = MAX(col, buffCols); + row = MIN(row, buffSize); + int const minrow = MIN(row, term.row), mincol = MIN(col, buffCols); + #elif COLUMNS_PATCH + int tmp = col; int minrow, mincol; - TCursor c; - tmp = col; if (!term.maxcol) term.maxcol = term.col; col = MAX(col, term.maxcol); minrow = MIN(row, term.row); mincol = MIN(col, term.maxcol); + #else + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + #endif // VIM_BROWSE_PATCH + int *bp; + TCursor c; + + #if KEYBOARDSELECT_PATCH + if ( row < term.row || col < term.col ) + toggle_winmode(trt_kbdselect(XK_Escape, NULL, 0)); + #endif // KEYBOARDSELECT_PATCH + if (col < 1 || row < 1) { fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); return; } + #if VIM_BROWSE_PATCH + if (alt) + tswapscreen(); + #endif // VIM_BROWSE_PATCH + /* * slide screen to keep cursor where we expect it - * tscrollup would work here, but we can optimize to * memmove because we're freeing the earlier lines */ for (i = 0; i <= term.c.y - row; i++) { + #if !VIM_BROWSE_PATCH free(term.line[i]); + #endif // VIM_BROWSE_PATCH free(term.alt[i]); } /* ensure that both src and dst are not NULL */ if (i > 0) { + #if !VIM_BROWSE_PATCH memmove(term.line, term.line + i, row * sizeof(Line)); + #endif // VIM_BROWSE_PATCH memmove(term.alt, term.alt + i, row * sizeof(Line)); } for (i += row; i < term.row; i++) { + #if !VIM_BROWSE_PATCH free(term.line[i]); + #endif // VIM_BROWSE_PATCH free(term.alt[i]); } /* resize to new height */ + #if VIM_BROWSE_PATCH + buf = xrealloc(buf, (buffSize + row) * sizeof(Line)); + mark = xrealloc(mark, col * row * sizeof(*mark)); + #else term.line = xrealloc(term.line, row * sizeof(Line)); + #endif // VIM_BROWSE_PATCH term.alt = xrealloc(term.alt, row * sizeof(Line)); term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + #if SCROLLBACK_PATCH + Glyph gc=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0}; for (i = 0; i < HISTSIZE; i++) { term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); - for (j = mincol; j < col; j++) { - term.hist[i][j] = term.c.attr; - term.hist[i][j].u = ' '; - } + for (j = mincol; j < col; j++) + term.hist[i][j] = gc; } + #endif // SCROLLBACK_PATCH /* resize each row to new width, zero-pad if needed */ for (i = 0; i < minrow; i++) { + #if !VIM_BROWSE_PATCH term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + #endif // VIM_BROWSE_PATCH term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); } /* allocate any new rows */ for (/* i = minrow */; i < row; i++) { + #if !VIM_BROWSE_PATCH term.line[i] = xmalloc(col * sizeof(Glyph)); + #endif // VIM_BROWSE_PATCH term.alt[i] = xmalloc(col * sizeof(Glyph)); } - if (col > term.maxcol) { + #if VIM_BROWSE_PATCH + if (col > buffCols) + #elif COLUMNS_PATCH + if (col > term.maxcol) + #else + if (col > term.col) + #endif // VIM_BROWSE_PATCH + { + #if VIM_BROWSE_PATCH + bp = term.tabs + buffCols; + memset(bp, 0, sizeof(*term.tabs) * (col - buffCols)); + #elif COLUMNS_PATCH bp = term.tabs + term.maxcol; - memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol)); + #else + bp = term.tabs + term.col; + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + #endif // VIM_BROWSE_PATCH + while (--bp > term.tabs && !*bp) /* nothing */ ; for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) *bp = 1; } + #if VIM_BROWSE_PATCH + Glyph g=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0}; + for (i = 0; i < buffSize; ++i) { + buf[i] = xrealloc(ini ? NULL : buf[i], col*sizeof(Glyph)); + for (int j = ini ? 0 : buffCols; j < col; ++j) buf[i][j] = g; + } + for (i = 0; i < row; ++i) buf[buffSize + i] = buf[i]; + term.line = &buf[*(histOp?&histOff:&insertOff) +=MAX(term.c.y-row+1,0)]; + memset(mark, 0, col * row * sizeof(*mark)); + #endif // VIM_BROWSE_PATCH /* update terminal size */ + #if VIM_BROWSE_PATCH + term.col = colSet; + buffCols = col; + #elif COLUMNS_PATCH term.col = tmp; term.maxcol = col; + #else + term.col = col; + #endif // VIM_BROWSE_PATCH term.row = row; + #if VIM_BROWSE_PATCH + if (alt) + tswapscreen(); + #endif // VIM_BROWSE_PATCH /* reset scrolling region */ tsetscroll(0, row-1); /* make use of the LIMIT in tmoveto */ @@ -2698,23 +3517,47 @@ tresize(int col, int row) void resettitle(void) { + #if CSI_22_23_PATCH + xsettitle(NULL, 0); + #else xsettitle(NULL); + #endif // CSI_22_23_PATCH } void drawregion(int x1, int y1, int x2, int y2) { + #if VIM_BROWSE_PATCH + if (altToggle && histMode && !histOp) + memset(term.dirty, 0, sizeof(*term.dirty) * term.row); + int const o = !IS_SET(MODE_ALTSCREEN) && histMode && !histOp, h =rows(); + #endif // VIM_BROWSE_PATCH int y; for (y = y1; y < y2; y++) { + #if VIM_BROWSE_PATCH + int const oy = o ? (y + insertOff - histOff + h) % h : y; + if (!BETWEEN(oy, 0, term.row-1) || !term.dirty[y]) continue; + xdrawline(term.line[y], x1, oy, x2); + #else if (!term.dirty[y]) continue; term.dirty[y] = 0; + #if SCROLLBACK_PATCH xdrawline(TLINE(y), x1, y, x2); + #else + xdrawline(term.line[y], x1, y, x2); + #endif // SCROLLBACK_PATCH + #endif // VIM_BROWSE_PATCH } + #if VIM_BROWSE_PATCH + memset(&term.dirty[y1], 0, sizeof(*term.dirty) * (y2 - y1)); + #endif // VIM_BROWSE_PATCH } +#include "patch/st_include.c" + void draw(void) { @@ -2731,11 +3574,26 @@ draw(void) if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) cx--; - drawregion(0, 0, term.col, term.row); - if (term.scr == 0) - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + #if VIM_BROWSE_PATCH + if (histMode) + historyPreDraw(); + #endif // VIM_BROWSE_PATCH + drawregion(0, 0, term.col, term.row); + + #if VIM_BROWSE_PATCH + if (!histMode) + #elif SCROLLBACK_PATCH + if (term.scr == 0) + #endif // SCROLLBACK_PATCH + #if LIGATURES_PATCH + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx], + term.line[term.ocy], term.col); + #else + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + #endif // LIGATURES_PATCH term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); diff --git a/st.desktop b/st.desktop index 2d2b76f..14a389a 100644 --- a/st.desktop +++ b/st.desktop @@ -1,12 +1,8 @@ [Desktop Entry] -Type=Application +Name=st +Comment=st is a simple terminal implementation for X Exec=st -TryExec=st Icon=utilities-terminal Terminal=false -Categories=System;TerminalEmulator; - -Name=st -GenericName=Terminal -Comment=st is a simple terminal implementation for X -StartupWMClass=st-256color +Type=Application +Categories=System;TerminalEmulator; \ No newline at end of file diff --git a/st.h b/st.h index c11ff99..41af307 100644 --- a/st.h +++ b/st.h @@ -2,23 +2,42 @@ #include #include +#include +#include +#include +#include +#include +#include +#include "patches.h" /* macros */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) #define LEN(a) (sizeof(a) / sizeof(a)[0]) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#if VIM_BROWSE_PATCH +#define OUT(x, a, b) ((a) <= (x) || (x) <= (b)) +#endif // VIM_BROWSE_PATCH #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#if LIGATURES_PATCH +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ + (a).fg != (b).fg || \ + (a).bg != (b).bg) +#else #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ (a).bg != (b).bg) +#endif // LIGATURES_PATCH #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) +#if SCROLLBACK_PATCH +#define HISTSIZE 2000 +#endif // SCROLLBACK_PATCH enum glyph_attribute { ATTR_NULL = 0, @@ -33,14 +52,49 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, + #if BOXDRAW_PATCH ATTR_BOXDRAW = 1 << 11, + #endif // BOXDRAW_PATCH + #if LIGATURES_PATCH + ATTR_LIGA = 1 << 12, + #endif // LIGATURES_PATCH + #if SIXEL_PATCH + ATTR_SIXEL = 1 << 13, + #endif // SIXEL_PATCH ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + #if UNDERCURL_PATCH + ATTR_DIRTYUNDERLINE = 1 << 15, + #endif // UNDERCURL_PATCH }; + +#if SIXEL_PATCH +typedef struct _ImageList { + struct _ImageList *next, *prev; + unsigned char *pixels; + void *pixmap; + int width; + int height; + int x; + int y; + int should_delete; +} ImageList; +#endif // SIXEL_PATCH + +#if WIDE_GLYPHS_PATCH enum drawing_mode { - DRAW_NONE = 0, - DRAW_BG = 1 << 0, - DRAW_FG = 1 << 1, + DRAW_NONE = 0, + DRAW_BG = 1 << 0, + DRAW_FG = 1 << 1, }; +#endif // WIDE_GLYPHS_PATCH + +/* Used to control which screen(s) keybindings and mouse shortcuts apply to. */ +enum screen { + S_PRI = -1, /* primary screen */ + S_ALL = 0, /* both primary and alt screen */ + S_ALT = 1 /* alternate screen */ +}; + enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, @@ -64,16 +118,65 @@ typedef unsigned short ushort; typedef uint_least32_t Rune; +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + #define Glyph Glyph_ typedef struct { Rune u; /* character code */ ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ + #if UNDERCURL_PATCH + int ustyle; /* underline style */ + int ucolor[3]; /* underline color */ + #endif // UNDERCURL_PATCH } Glyph; typedef Glyph *Line; +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + #if COLUMNS_PATCH && !VIM_BROWSE_PATCH + int maxcol; + #endif // COLUMNS_PATCH + Line *line; /* screen */ + Line *alt; /* alternate screen */ + #if SCROLLBACK_PATCH + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int histn; /* number of history entries */ + int scr; /* scroll back */ + #endif // SCROLLBACK_PATCH + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + #if SIXEL_PATCH + ImageList *images; /* sixel images */ + ImageList *images_alt; /* sixel images for alternate screen */ + #endif // SIXEL_PATCH + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + typedef union { int i; uint ui; @@ -82,20 +185,145 @@ typedef union { const char *s; } Arg; +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + #if BACKGROUND_IMAGE_PATCH + int x, y; /* window location */ + #endif // BACKGROUND_IMAGE_PATCH + #if ANYSIZE_PATCH + int hborderpx, vborderpx; + #endif // ANYSIZE_PATCH + int ch; /* char height */ + int cw; /* char width */ + #if VERTCENTER_PATCH + int cyo; /* char y offset */ + #endif // VERTCENTER_PATCH + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + #if FULLSCREEN_PATCH + Atom netwmstate, netwmfullscreen; + #endif // FULLSCREEN_PATCH + #if NETWMICON_PATCH + Atom netwmicon; + #endif // NETWMICON_PATCH + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + #if BACKGROUND_IMAGE_PATCH + GC bggc; /* Graphics Context for background */ + #endif // BACKGROUND_IMAGE_PATCH + Visual *vis; + XSetWindowAttributes attrs; + #if HIDECURSOR_PATCH || OPENURLONCLICK_PATCH + /* Here, we use the term *pointer* to differentiate the cursor + * one sees when hovering the mouse over the terminal from, e.g., + * a green rectangle where text would be entered. */ + Cursor vpointer, bpointer; /* visible and hidden pointers */ + int pointerisvisible; + #endif // HIDECURSOR_PATCH + #if OPENURLONCLICK_PATCH + Cursor upointer; + #endif // OPENURLONCLICK_PATCH + int scr; + int isfixed; /* is fixed geometry? */ + #if ALPHA_PATCH + int depth; /* bit depth */ + #endif // ALPHA_PATCH + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; + int screen; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; + int screen; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + void die(const char *, ...); void redraw(void); void draw(void); +void drawregion(int, int, int, int); +void tfulldirt(void); -void kscrolldown(const Arg *); -void kscrollup(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); void toggleprinter(const Arg *); int tattrset(int); +int tisaltscr(void); void tnew(int, int); void tresize(int, int); +#if VIM_BROWSE_PATCH +void tmoveto(int x, int y); +#endif // VIM_BROWSE_PATCH void tsetdirtattr(int); void ttyhangup(void); int ttynew(const char *, char *, const char *, char **); @@ -118,13 +346,17 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + +#if BOXDRAW_PATCH int isboxdraw(Rune); ushort boxdrawindex(const Glyph *); #ifdef XFT_VERSION /* only exposed to x.c, otherwise we'll need Xft.h for the types */ void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); -#endif +#endif // XFT_VERSION +#endif // BOXDRAW_PATCH /* config.h globals */ extern char *utmp; @@ -139,5 +371,22 @@ extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; extern unsigned int defaultcs; -extern float alpha; +#if EXTERNALPIPE_PATCH +extern int extpipeactive; +#endif // EXTERNALPIPE_PATCH + +#if BOXDRAW_PATCH extern const int boxdraw, boxdraw_bold, boxdraw_braille; +#endif // BOXDRAW_PATCH +#if ALPHA_PATCH +extern float alpha; +#if ALPHA_FOCUS_HIGHLIGHT_PATCH +extern float alphaUnfocused; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH +#endif // ALPHA_PATCH + +extern DC dc; +extern XWindow xw; +extern XSelection xsel; +extern TermWindow win; +extern Term term; diff --git a/st.info b/st.info index 8201ad6..b286fb5 100644 --- a/st.info +++ b/st.info @@ -1,4 +1,6 @@ st-mono| simpleterm monocolor, +# undercurl patch / UNDERCURL_PATCH + Su, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, am, bce, @@ -33,6 +35,7 @@ st-mono| simpleterm monocolor, el=\E[K, el1=\E[1K, enacs=\E)0, + E3=\E[3J, flash=\E[?5h$<80/>\E[?5l, fsl=^G, home=\E[H, @@ -161,7 +164,9 @@ st-mono| simpleterm monocolor, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, - rmcup=\E[?1049l, +# CSI 22, 23 patch / CSI_22_23_PATCH +# rmcup=\E[?1049l, + rmcup=\E[?1049l\E[23;0;0t, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[27m, @@ -172,7 +177,9 @@ st-mono| simpleterm monocolor, sitm=\E[3m, sgr0=\E[0m, smacs=\E(0, - smcup=\E[?1049h, +# CSI 22, 23 patch / CSI_22_23_PATCH +# smcup=\E[?1049h, + smcup=\E[?1049h\E[22;0;0t, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[7m, @@ -184,6 +191,10 @@ st-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, + BE=\E[?2004h, + BD=\E[?2004l, + PS=\E[200~, + PE=\E[201~, # disabled rep for now: causes some issues with older ncurses versions. # rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) @@ -191,6 +202,8 @@ st-mono| simpleterm monocolor, Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, +# sync patch / SYNC_PATCH + Sync=\EP=%p1%ds\E\\, st| simpleterm, use=st-mono, diff --git a/st.o b/st.o index 476caf691b436ae3ec6a8206c4f6f5793f36398d..cf7e1fe4a846d166b45b6dc6fd1206ca825d21c7 100644 GIT binary patch literal 83096 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 literal 81560 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 -#include -#include -#include -#include -#include -#include #include -#include +#include #include #include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include char *argv0; #include "arg.h" #include "st.h" #include "win.h" +#if LIGATURES_PATCH +#include "hb.h" +#endif // LIGATURES_PATCH -/* types used in config.h */ -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; +#if THEMED_CURSOR_PATCH +#include +#endif // THEMED_CURSOR_PATCH -typedef struct { - uint mod; - uint button; - void (*func)(const Arg *); - const Arg arg; - uint release; -} MouseShortcut; - -typedef struct { - KeySym k; - uint mask; - char *s; - /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ - signed char appkey; /* application keypad */ - signed char appcursor; /* application cursor */ -} Key; - -/* Xresources preferences */ -enum resource_type { STRING = 0, INTEGER = 1, FLOAT = 2 }; - -typedef struct { - char *name; - enum resource_type type; - void *dst; -} ResourcePref; +#if UNDERCURL_PATCH +/* Undercurl slope types */ +enum undercurl_slope_type { + UNDERCURL_SLOPE_ASCENDING = 0, + UNDERCURL_SLOPE_TOP_CAP = 1, + UNDERCURL_SLOPE_DESCENDING = 2, + UNDERCURL_SLOPE_BOTTOM_CAP = 3 +}; +#endif // UNDERCURL_PATCH /* X modifiers */ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1 << 13 | 1 << 14) +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); +static void ttysend(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); -static void ttysend(const Arg *); + +#include "patch/st_include.h" +#include "patch/x_include.h" /* config.h for applying patches and the configuration. */ #include "config.h" +#if CSI_22_23_PATCH +/* size of title stack */ +#define TITLESTACKSIZE 8 +#endif // CSI_22_23_PATCH + /* XEMBED messages */ -#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 /* macros */ -#define IS_SET(flag) ((win.mode & (flag)) != 0) -#define TRUERED(x) (((x)&0xff0000) >> 8) -#define TRUEGREEN(x) (((x)&0xff00)) -#define TRUEBLUE(x) (((x)&0xff) << 8) - -typedef XftDraw *Draw; -typedef XftColor Color; -typedef XftGlyphFontSpec GlyphFontSpec; - -/* Purely graphic info */ -typedef struct { - int tw, th; /* tty width and height */ - int w, h; /* window width and height */ - int hborderpx, vborderpx; - int ch; /* char height */ - int cw; /* char width */ - int cyo; /* char y offset */ - int mode; /* window state/mode flags */ - int cursor; /* cursor style */ -} TermWindow; - -typedef struct { - Display *dpy; - Colormap cmap; - Window win; - Drawable buf; - GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; - struct { - XIM xim; - XIC xic; - XPoint spot; - XVaNestedList spotlist; - } ime; - Draw draw; - Visual *vis; - XSetWindowAttributes attrs; - int scr; - int isfixed; /* is fixed geometry? */ - int depth; /* bit depth */ - int l, t; /* left and top offset */ - int gm; /* geometry mask */ -} XWindow; - -typedef struct { - Atom xtarget; - char *primary, *clipboard; - struct timespec tclick1; - struct timespec tclick2; -} XSelection; - -/* Font structure */ -#define Font Font_ -typedef struct { - int height; - int width; - int ascent; - int descent; - int badslant; - int badweight; - short lbearing; - short rbearing; - XftFont *match; - FcFontSet *set; - FcPattern *pattern; -} Font; - -/* Drawing Context */ -typedef struct { - Color *col; - size_t collen; - Font font, bfont, ifont, ibfont; - GC gc; -} DC; +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) static inline ushort sixd_to_16bit(int); -static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, - int); -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, - int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +#if WIDE_GLYPHS_PATCH +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); +#else +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +#endif // WIDE_GLYPHS_PATCH +#if LIGATURES_PATCH +static void xresetfontsettings(ushort mode, Font **font, int *frcflags); +#endif // LIGATURES_PATCH static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); @@ -172,8 +97,6 @@ static void xhints(void); static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); static void xloadfonts(const char *, double); -static int xloadsparefont(FcPattern *, int); -static void xloadsparefonts(void); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); @@ -189,7 +112,6 @@ static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); static uint buttonmask(uint); -static int mouseaction(XEvent *, uint); static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); @@ -198,6 +120,10 @@ static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); static void setsel(char *, Time); +#if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH +static void sigusr1_reload(int sig); +#endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH +static int mouseaction(XEvent *, uint); static void mousesel(XEvent *, int); static void mousereport(XEvent *); static char *kmap(KeySym, uint); @@ -207,44 +133,59 @@ static void run(void); static void usage(void); static void (*handler[LASTEvent])(XEvent *) = { - [KeyPress] = kpress, - [ClientMessage] = cmessage, - [ConfigureNotify] = resize, - [VisibilityNotify] = visibility, - [UnmapNotify] = unmap, - [Expose] = expose, - [FocusIn] = focus, - [FocusOut] = focus, - [MotionNotify] = bmotion, - [ButtonPress] = bpress, - [ButtonRelease] = brelease, - /* - * Uncomment if you want the selection to disappear when you select - * something different in another window. - */ - /* [SelectionClear] = selclear_, */ - [SelectionNotify] = selnotify, - /* - * PropertyNotify is only turned on when there is some INCR transfer - * happening for the selection retrieval. - */ - [PropertyNotify] = propnotify, - [SelectionRequest] = selrequest, + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, + #if ST_EMBEDDER_PATCH + [CreateNotify] = createnotify, + [DestroyNotify] = destroynotify, + #endif // ST_EMBEDDER_PATCH }; /* Globals */ -static DC dc; -static XWindow xw; -static XSelection xsel; -static TermWindow win; +Term term; +DC dc; +XWindow xw; +XSelection xsel; +TermWindow win; + +#if CSI_22_23_PATCH +static int tstki; /* title stack index */ +static char *titlestack[TITLESTACKSIZE]; /* title stack */ +#endif // CSI_22_23_PATCH /* Font Ring Cache */ -enum { FRC_NORMAL, FRC_ITALIC, FRC_BOLD, FRC_ITALICBOLD }; +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; typedef struct { - XftFont *font; - int flags; - Rune unicodep; + XftFont *font; + int flags; + Rune unicodep; } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ @@ -255,1938 +196,3470 @@ static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; +#if ALPHA_PATCH static char *opt_alpha = NULL; +#endif // ALPHA_PATCH static char *opt_class = NULL; -static char **opt_cmd = NULL; +static char **opt_cmd = NULL; static char *opt_embed = NULL; -static char *opt_font = NULL; -static char *opt_io = NULL; -static char *opt_line = NULL; -static char *opt_name = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; static char *opt_title = NULL; +#if WORKINGDIR_PATCH +static char *opt_dir = NULL; +#endif // WORKINGDIR_PATCH + +#if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH +static int focused = 0; +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH static uint buttons; /* bit field of pressed buttons */ +#if BLINKING_CURSOR_PATCH +static int cursorblinks = 0; +#endif // BLINKING_CURSOR_PATCH +#if VISUALBELL_1_PATCH +static int bellon = 0; /* visual bell status */ +#endif // VISUALBELL_1_PATCH +#if RELATIVEBORDER_PATCH +int borderpx; +#endif // RELATIVEBORDER_PATCH +#if SWAPMOUSE_PATCH +static Cursor cursor; +static XColor xmousefg, xmousebg; +#endif // SWAPMOUSE_PATCH -void clipcopy(const Arg *dummy) { - Atom clipboard; +#include "patch/x_include.c" - free(xsel.clipboard); - xsel.clipboard = NULL; +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; - if (xsel.primary != NULL) { - xsel.clipboard = xstrdup(xsel.primary); - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); - } + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } } -void clippaste(const Arg *dummy) { - Atom clipboard; +void +clippaste(const Arg *dummy) +{ + Atom clipboard; - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, xw.win, - CurrentTime); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); } -void selpaste(const Arg *dummy) { - XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, xw.win, - CurrentTime); +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; } -void numlock(const Arg *dummy) { win.mode ^= MODE_NUMLOCK; } - -void zoom(const Arg *arg) { - Arg larg; - - larg.f = usedfontsize + arg->f; - zoomabs(&larg); +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); } -void zoomabs(const Arg *arg) { - xunloadfonts(); - xloadfonts(usedfont, arg->f); - xloadsparefonts(); - cresize(0, 0); - redraw(); - xhints(); +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); } -void zoomreset(const Arg *arg) { - Arg larg; +void +zoom(const Arg *arg) +{ + Arg larg; - if (defaultfontsize > 0) { - larg.f = defaultfontsize; - zoomabs(&larg); - } + larg.f = usedfontsize + arg->f; + zoomabs(&larg); } -void ttysend(const Arg *arg) { ttywrite(arg->s, strlen(arg->s), 1); } - -int evcol(XEvent *e) { - int x = e->xbutton.x - win.hborderpx; - LIMIT(x, 0, win.tw - 1); - return x / win.cw; +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + #if FONT2_PATCH + xloadsparefonts(); + #endif // FONT2_PATCH + cresize(0, 0); + redraw(); + xhints(); } -int evrow(XEvent *e) { - int y = e->xbutton.y - win.vborderpx; - LIMIT(y, 0, win.th - 1); - return y / win.ch; +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } } -void mousesel(XEvent *e, int done) { - int type, seltype = SEL_REGULAR; - uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); - - for (type = 1; type < LEN(selmasks); ++type) { - if (match(selmasks[type], state)) { - seltype = type; - break; - } - } - selextend(evcol(e), evrow(e), seltype, done); - if (done) - setsel(getsel(), e->xbutton.time); +int +evcol(XEvent *e) +{ + #if ANYSIZE_PATCH + int x = e->xbutton.x - win.hborderpx; + #else + int x = e->xbutton.x - borderpx; + #endif // ANYSIZE_PATCH + LIMIT(x, 0, win.tw - 1); + return x / win.cw; } -void mousereport(XEvent *e) { - int len, btn, code; - int x = evcol(e), y = evrow(e); - int state = e->xbutton.state; - char buf[40]; - static int ox, oy; - - if (e->type == MotionNotify) { - if (x == ox && y == oy) - return; - if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) - return; - /* MODE_MOUSEMOTION: no reporting if no button is pressed */ - if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) - return; - /* Set btn to lowest-numbered pressed button, or 12 if no - * buttons are pressed. */ - for (btn = 1; btn <= 11 && !(buttons & (1 << (btn - 1))); btn++) - ; - code = 32; - } else { - btn = e->xbutton.button; - /* Only buttons 1 through 11 can be encoded */ - if (btn < 1 || btn > 11) - return; - if (e->type == ButtonRelease) { - /* MODE_MOUSEX10: no button release reporting */ - if (IS_SET(MODE_MOUSEX10)) - return; - /* Don't send release events for the scroll wheel */ - if (btn == 4 || btn == 5) - return; - } - code = 0; - } - - ox = x; - oy = y; - - /* Encode btn into code. If no button is pressed for a motion event in - * MODE_MOUSEMANY, then encode it as a release. */ - if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) - code += 3; - else if (btn >= 8) - code += 128 + btn - 8; - else if (btn >= 4) - code += 64 + btn - 4; - else - code += btn - 1; - - if (!IS_SET(MODE_MOUSEX10)) { - code += ((state & ShiftMask) ? 4 : 0) + - ((state & Mod1Mask) ? 8 : 0) /* meta key: alt */ - + ((state & ControlMask) ? 16 : 0); - } - - if (IS_SET(MODE_MOUSESGR)) { - len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", code, x + 1, y + 1, - e->type == ButtonRelease ? 'm' : 'M'); - } else if (x < 223 && y < 223) { - len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 32 + code, 32 + x + 1, - 32 + y + 1); - } else { - return; - } - - ttywrite(buf, len, 0); +int +evrow(XEvent *e) +{ + #if ANYSIZE_PATCH + int y = e->xbutton.y - win.vborderpx; + #else + int y = e->xbutton.y - borderpx; + #endif // ANYSIZE_PATCH + LIMIT(y, 0, win.th - 1); + return y / win.ch; } -uint buttonmask(uint button) { - return button == Button1 ? Button1Mask - : button == Button2 ? Button2Mask - : button == Button3 ? Button3Mask - : button == Button4 ? Button4Mask - : button == Button5 ? Button5Mask - : 0; +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; } -int mouseaction(XEvent *e, uint release) { - MouseShortcut *ms; +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + int screen = tisaltscr() ? S_ALT : S_PRI; - /* ignore Buttonmask for Button - it's set on release */ - uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (ms->release == release && ms->button == e->xbutton.button && - (match(ms->mod, state) || /* exact or forced */ - match(ms->mod, state & ~forcemousemod))) { - ms->func(&(ms->arg)); - return 1; - } - } + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (!ms->screen || (ms->screen == screen)) && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } - return 0; + return 0; } -void bpress(XEvent *e) { - int btn = e->xbutton.button; - struct timespec now; - int snap; +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); - if (1 <= btn && btn <= 11) - buttons |= 1 << (btn - 1); - - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - if (mouseaction(e, 0)) - return; - - if (btn == Button1) { - /* - * If the user clicks below predefined timeouts specific - * snapping behaviour is exposed. - */ - clock_gettime(CLOCK_MONOTONIC, &now); - if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { - snap = SNAP_LINE; - } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { - snap = SNAP_WORD; - } else { - snap = 0; - } - xsel.tclick2 = xsel.tclick1; - xsel.tclick1 = now; - - selstart(evcol(e), evrow(e), snap); - } + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); } -void propnotify(XEvent *e) { - XPropertyEvent *xpev; - Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); +void +mousereport(XEvent *e) +{ + int len, btn, code; + int x = evcol(e), y = evrow(e); + int state = e->xbutton.state; + char buf[40]; + static int ox, oy; - xpev = &e->xproperty; - if (xpev->state == PropertyNewValue && - (xpev->atom == XA_PRIMARY || xpev->atom == clipboard)) { - selnotify(e); - } + if (e->type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MODE_MOUSEMOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) + return; + + /* Set btn to lowest-numbered pressed button, or 12 if no + * buttons are pressed. */ + for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) + ; + code = 32; + } else { + btn = e->xbutton.button; + /* Only buttons 1 through 11 can be encoded */ + if (btn < 1 || btn > 11) + return; + if (e->type == ButtonRelease) { + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + /* Don't send release events for the scroll wheel */ + if (btn == 4 || btn == 5) + return; + } + code = 0; + } + + ox = x; + oy = y; + + /* Encode btn into code. If no button is pressed for a motion event in + * MODE_MOUSEMANY, then encode it as a release. */ + if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) + code += 3; + else if (btn >= 8) + code += 128 + btn - 8; + else if (btn >= 4) + code += 64 + btn - 4; + else + code += btn - 1; + + if (!IS_SET(MODE_MOUSEX10)) { + code += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + code, x+1, y+1, + e->type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+code, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); } -void selnotify(XEvent *e) { - ulong nitems, ofs, rem; - int format; - uchar *data, *last, *repl; - Atom type, incratom, property = None; +void +bpress(XEvent *e) +{ + int btn = e->xbutton.button; + struct timespec now; + #if !VIM_BROWSE_PATCH + int snap; + #endif // VIM_BROWSE_PATCH - incratom = XInternAtom(xw.dpy, "INCR", 0); + if (1 <= btn && btn <= 11) + buttons |= 1 << (btn-1); - ofs = 0; - if (e->type == SelectionNotify) - property = e->xselection.property; - else if (e->type == PropertyNotify) - property = e->xproperty.atom; + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } - if (property == None) - return; + if (mouseaction(e, 0)) + return; - do { - if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, BUFSIZ / 4, False, - AnyPropertyType, &type, &format, &nitems, &rem, - &data)) { - fprintf(stderr, "Clipboard allocation failed\n"); - return; - } + if (btn == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + #if VIM_BROWSE_PATCH + int const tripleClick = TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout, + doubleClick = TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout; + if ((mouseYank || mouseSelect) && (tripleClick || doubleClick)) { + if (!IS_SET(MODE_NORMAL)) normalMode(); + historyOpToggle(1, 1); + tmoveto(evcol(e), evrow(e)); + if (tripleClick) { + if (mouseYank) pressKeys("dVy", 3); + if (mouseSelect) pressKeys("dV", 2); + } else if (doubleClick) { + if (mouseYank) pressKeys("dyiW", 4); + if (mouseSelect) { + tmoveto(evcol(e), evrow(e)); + pressKeys("viW", 3); + } + } + historyOpToggle(-1, 1); + } else { + if (!IS_SET(MODE_NORMAL)) selstart(evcol(e), evrow(e), 0); + else { + historyOpToggle(1, 1); + tmoveto(evcol(e), evrow(e)); + pressKeys("v", 1); + historyOpToggle(-1, 1); + } + } + #else + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + #endif // VIM_BROWSE_PATCH + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { - /* - * If there is some PropertyNotify with no data, then - * this is the signal of the selection owner that all - * data has been transferred. We won't need to receive - * PropertyNotify events anymore. - */ - MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); - } + #if !VIM_BROWSE_PATCH + selstart(evcol(e), evrow(e), snap); + #endif // VIM_BROWSE_PATCH - if (type == incratom) { - /* - * Activate the PropertyNotify events so we receive - * when the selection owner does send us the next - * chunk of data. - */ - MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); - - /* - * Deleting the property is the transfer start signal. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); - continue; - } - - /* - * As seen in getsel: - * Line endings are inconsistent in the terminal and GUI world - * copy and pasting. When receiving some selection data, - * replace all '\n' with '\r'. - * FIXME: Fix the computer world. - */ - repl = data; - last = data + nitems * format / 8; - while ((repl = memchr(repl, '\n', last - repl))) { - *repl++ = '\r'; - } - - if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) - ttywrite("\033[200~", 6, 0); - ttywrite((char *)data, nitems * format / 8, 1); - if (IS_SET(MODE_BRCKTPASTE) && rem == 0) - ttywrite("\033[201~", 6, 0); - XFree(data); - /* number of 32-bit chunks returned */ - ofs += nitems * format / 32; - } while (rem > 0); - - /* - * Deleting the property again tells the selection owner to send the - * next data chunk in the property. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); + #if OPENURLONCLICK_PATCH + clearurl(); + url_click = 1; + #endif // OPENURLONCLICK_PATCH + } } -void xclipcopy(void) { clipcopy(NULL); } +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); -void selclear_(XEvent *e) { selclear(); } + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } -void selrequest(XEvent *e) { - XSelectionRequestEvent *xsre; - XSelectionEvent xev; - Atom xa_targets, string, clipboard; - char *seltext; - - xsre = (XSelectionRequestEvent *)e; - xev.type = SelectionNotify; - xev.requestor = xsre->requestor; - xev.selection = xsre->selection; - xev.target = xsre->target; - xev.time = xsre->time; - if (xsre->property == None) - xsre->property = xsre->target; - - /* reject */ - xev.property = None; - - xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); - if (xsre->target == xa_targets) { - /* respond with the supported type */ - string = xsel.xtarget; - XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_ATOM, 32, - PropModeReplace, (uchar *)&string, 1); - xev.property = xsre->property; - } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { - /* - * xith XA_STRING non ascii characters may be incorrect in the - * requestor. It is not our problem, use utf8. - */ - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - if (xsre->selection == XA_PRIMARY) { - seltext = xsel.primary; - } else if (xsre->selection == clipboard) { - seltext = xsel.clipboard; - } else { - fprintf(stderr, "Unhandled clipboard selection 0x%lx\n", xsre->selection); - return; - } - if (seltext != NULL) { - XChangeProperty(xsre->display, xsre->requestor, xsre->property, - xsre->target, 8, PropModeReplace, (uchar *)seltext, - strlen(seltext)); - xev.property = xsre->property; - } - } - - /* all done, send a notification to the listener */ - if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *)&xev)) - fprintf(stderr, "Error sending SelectionNotify event\n"); + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency && + !strncmp(XGetAtomName(xw.dpy, e->xproperty.atom), "_NET_WM_STATE", 13)) { + updatexy(); + redraw(); + } + #endif // BACKGROUND_IMAGE_PATCH } -void setsel(char *str, Time t) { - if (!str) - return; +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; - free(xsel.primary); - xsel.primary = str; + incratom = XInternAtom(xw.dpy, "INCR", 0); - XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); - if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear(); + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + #if BACKGROUND_IMAGE_PATCH + if (e->type == PropertyNotify && nitems == 0 && rem == 0 && !pseudotransparency) + #else + if (e->type == PropertyNotify && nitems == 0 && rem == 0) + #endif // BACKGROUND_IMAGE_PATCH + { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + #if BACKGROUND_IMAGE_PATCH + if (!pseudotransparency) { + #endif // BACKGROUND_IMAGE_PATCH + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + #if BACKGROUND_IMAGE_PATCH + } + #endif // BACKGROUND_IMAGE_PATCH + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); } -void xsetsel(char *str) { setsel(str, CurrentTime); } - -void brelease(XEvent *e) { - int btn = e->xbutton.button; - - if (1 <= btn && btn <= 11) - buttons &= ~(1 << (btn - 1)); - - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - if (mouseaction(e, 1)) - return; - if (btn == Button1) - mousesel(e, 1); +void +xclipcopy(void) +{ + clipcopy(NULL); } -void bmotion(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - mousesel(e, 0); +void +selclear_(XEvent *e) +{ + selclear(); } -void cresize(int width, int height) { - int col, row; +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; - if (width != 0) - win.w = width; - if (height != 0) - win.h = height; + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; - col = (win.w - 2 * borderpx) / win.cw; - row = (win.h - 2 * borderpx) / win.ch; - col = MAX(1, col); - row = MAX(1, row); + /* reject */ + xev.property = None; - win.hborderpx = (win.w - col * win.cw) / 2; - win.vborderpx = (win.h - row * win.ch) / 2; + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } - tresize(col, row); - xresize(col, row); - ttyresize(win.tw, win.th); + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); } -void xresize(int col, int row) { - win.tw = col * win.cw; - win.th = row * win.ch; +void +setsel(char *str, Time t) +{ + if (!str) + return; - XFreePixmap(xw.dpy, xw.buf); - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - XftDrawChange(xw.draw, xw.buf); - xclear(0, 0, win.w, win.h); + free(xsel.primary); + xsel.primary = str; - /* resize to new width */ - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); + + #if CLIPBOARD_PATCH + clipcopy(NULL); + #endif // CLIPBOARD_PATCH } -ushort sixd_to_16bit(int x) { return x == 0 ? 0 : 0x3737 + 0x2828 * x; } +#if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH +void +sigusr1_reload(int sig) +{ + #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH + reload_config(sig); + #endif // XRESOURCES_RELOAD_PATCH + #if BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH + reload_image(); + #endif // BACKGROUND_IMAGE_RELOAD_PATCH + signal(SIGUSR1, sigusr1_reload); +} +#endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH -int xloadcolor(int i, const char *name, Color *ncolor) { - XRenderColor color = {.alpha = 0xffff}; - - if (!name) { - if (BETWEEN(i, 16, 255)) { /* 256 color */ - if (i < 6 * 6 * 6 + 16) { /* same colors as xterm */ - color.red = sixd_to_16bit(((i - 16) / 36) % 6); - color.green = sixd_to_16bit(((i - 16) / 6) % 6); - color.blue = sixd_to_16bit(((i - 16) / 1) % 6); - } else { /* greyscale */ - color.red = 0x0808 + 0x0a0a * (i - (6 * 6 * 6 + 16)); - color.green = color.blue = color.red; - } - return XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, ncolor); - } else - name = colorname[i]; - } - - return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); } -void xloadcols(void) { - int i; - static int loaded; - Color *cp; +void +brelease(XEvent *e) +{ + int btn = e->xbutton.button; - if (loaded) { - for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) - XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); - } else { - dc.collen = MAX(LEN(colorname), 256); - dc.col = xmalloc(dc.collen * sizeof(Color)); - } + if (1 <= btn && btn <= 11) + buttons &= ~(1 << (btn-1)); - for (i = 0; i < dc.collen; i++) - if (!xloadcolor(i, NULL, &dc.col[i])) { - if (colorname[i]) - die("could not allocate color '%s'\n", colorname[i]); - else - die("could not allocate color %d\n", i); - } + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } - /* set alpha value of bg color */ - if (opt_alpha) - alpha = strtof(opt_alpha, NULL); - dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); - dc.col[defaultbg].pixel &= 0x00FFFFFF; - dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; - loaded = 1; + if (mouseaction(e, 1)) + return; + #if VIM_BROWSE_PATCH + if (btn == Button1 && !IS_SET(MODE_NORMAL)) { + mousesel(e, 1); + #if OPENURLONCLICK_PATCH + if (url_click && e->xkey.state & url_opener_modkey) + openUrlOnClick(evcol(e), evrow(e), url_opener); + #endif // OPENURLONCLICK_PATCH + } + #else + if (btn == Button1) { + mousesel(e, 1); + #if OPENURLONCLICK_PATCH + if (url_click && e->xkey.state & url_opener_modkey) + openUrlOnClick(evcol(e), evrow(e), url_opener); + #endif // OPENURLONCLICK_PATCH + } + #endif // VIM_BROWSE_PATCH + #if RIGHTCLICKTOPLUMB_PATCH + else if (btn == Button3) + plumb(xsel.primary); + #endif // RIGHTCLICKTOPLUMB_PATCH } -int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) { - if (!BETWEEN(x, 0, dc.collen)) - return 1; +void +bmotion(XEvent *e) +{ + #if HIDECURSOR_PATCH + if (!xw.pointerisvisible) { + #if SWAPMOUSE_PATCH + if (win.mode & MODE_MOUSE) + XUndefineCursor(xw.dpy, xw.win); + else + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + #else + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + #endif // SWAPMOUSE_PATCH + xw.pointerisvisible = 1; + if (!IS_SET(MODE_MOUSEMANY)) + xsetpointermotion(0); + } + #endif // HIDECURSOR_PATCH + #if OPENURLONCLICK_PATCH + #if VIM_BROWSE_PATCH + if (!IS_SET(MODE_NORMAL)) + #endif // VIM_BROWSE_PATCH + if (!IS_SET(MODE_MOUSE)) { + if (!(e->xbutton.state & Button1Mask) && detecturl(evcol(e), evrow(e), 1)) + XDefineCursor(xw.dpy, xw.win, xw.upointer); + else + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + } + url_click = 0; + #endif // OPENURLONCLICK_PATCH - *r = dc.col[x].color.red >> 8; - *g = dc.col[x].color.green >> 8; - *b = dc.col[x].color.blue >> 8; + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } - return 0; + mousesel(e, 0); } -int xsetcolorname(int x, const char *name) { - Color ncolor; +void +cresize(int width, int height) +{ + int col, row; - if (!BETWEEN(x, 0, dc.collen)) - return 1; + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; - if (!xloadcolor(x, name, &ncolor)) - return 1; + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); - XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); - dc.col[x] = ncolor; + #if ANYSIZE_PATCH + win.hborderpx = (win.w - col * win.cw) / 2; + win.vborderpx = (win.h - row * win.ch) / 2; + #endif // ANYSIZE_PATCH - return 0; + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + #if !SINGLE_DRAWABLE_BUFFER_PATCH + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + #if ALPHA_PATCH + xw.depth + #else + DefaultDepth(xw.dpy, xw.scr) + #endif // ALPHA_PATCH + ); + XftDrawChange(xw.draw, xw.buf); + #endif // SINGLE_DRAWABLE_BUFFER_PATCH + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +#if VIM_BROWSE_PATCH +void normalMode() +{ + #if OPENURLONCLICK_PATCH + clearurl(); + restoremousecursor(); + #endif // OPENURLONCLICK_PATCH + historyModeToggle((win.mode ^=MODE_NORMAL) & MODE_NORMAL); +} +#endif // VIM_BROWSE_PATCH + +#if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH +void +xloadalpha(void) +{ + float const usedAlpha = focused ? alpha : alphaUnfocused; + if (opt_alpha) alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * usedAlpha) << 24; +} +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + +#if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH +void +xloadcols(void) +{ + static int loaded; + Color *cp; + + if (!loaded) { + dc.collen = 1 + (defaultbg = MAX(LEN(colorname), 256)); + dc.col = xmalloc((dc.collen) * sizeof(Color)); + } + + for (int i = 0; i+1 < dc.collen; ++i) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + if (dc.collen) // cannot die, as the color is already loaded. + xloadcolor(focused ? bg : bgUnfocused, NULL, &dc.col[defaultbg]); + + xloadalpha(); + loaded = 1; +} +#else +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + #if ALPHA_PATCH + /* set alpha value of bg color */ + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + #endif // ALPHA_PATCH + loaded = 1; +} +#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen - 1)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen - 1)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + #if ALPHA_PATCH + /* set alpha value of bg color */ + if (x == defaultbg) { + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; + } + #endif // ALPHA_PATCH + return 0; } /* * Absolute coordinates. */ -void xclear(int x1, int y1, int x2, int y2) { - XftDrawRect(xw.draw, &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg], - x1, y1, x2 - x1, y2 - y1); +void +xclear(int x1, int y1, int x2, int y2) +{ + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency) + XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y); + XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1); + #elif INVERT_PATCH + Color c; + c = dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg]; + if (invertcolors) { + c = invertedcolor(&c); + } + XftDrawRect(xw.draw, &c, x1, y1, x2-x1, y2-y1); + #else + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); + #endif // INVERT_PATCH } -void xhints(void) { - XClassHint class = {opt_name ? opt_name : termname, - opt_class ? opt_class : termname}; - XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh; - - sizeh = XAllocSizeHints(); - - sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; - sizeh->height = win.h; - sizeh->width = win.w; - sizeh->height_inc = 1; - sizeh->width_inc = 1; - sizeh->base_height = 2 * borderpx; - sizeh->base_width = 2 * borderpx; - sizeh->min_height = win.ch + 2 * borderpx; - sizeh->min_width = win.cw + 2 * borderpx; - if (xw.isfixed) { - sizeh->flags |= PMaxSize; - sizeh->min_width = sizeh->max_width = win.w; - sizeh->min_height = sizeh->max_height = win.h; - } - if (xw.gm & (XValue | YValue)) { - sizeh->flags |= USPosition | PWinGravity; - sizeh->x = xw.l; - sizeh->y = xw.t; - sizeh->win_gravity = xgeommasktogravity(xw.gm); - } - - XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class); - XFree(sizeh); +void +xclearwin(void) +{ + xclear(0, 0, win.w, win.h); } -int xgeommasktogravity(int mask) { - switch (mask & (XNegative | YNegative)) { - case 0: - return NorthWestGravity; - case XNegative: - return NorthEastGravity; - case YNegative: - return SouthWestGravity; - } +void +xhints(void) +{ + #if XRESOURCES_PATCH + XClassHint class = {opt_name ? opt_name : "st", + opt_class ? opt_class : "St"}; + #else + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + #endif // XRESOURCES_PATCH + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; - return SouthEastGravity; + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + #if ANYSIZE_PATCH || ANYSIZE_SIMPLE_PATCH + sizeh->height_inc = 1; + sizeh->width_inc = 1; + #else + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + #endif // ANYSIZE_PATCH + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); } -int xloadfont(Font *f, FcPattern *pattern) { - FcPattern *configured; - FcPattern *match; - FcResult result; - XGlyphInfo extents; - int wantattr, haveattr; +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } - /* - * Manually configure instead of calling XftMatchFont - * so that we can use the configured pattern for - * "missing glyph" lookups. - */ - configured = FcPatternDuplicate(pattern); - if (!configured) - return 1; - - FcConfigSubstitute(NULL, configured, FcMatchPattern); - XftDefaultSubstitute(xw.dpy, xw.scr, configured); - - match = FcFontMatch(NULL, configured, &result); - if (!match) { - FcPatternDestroy(configured); - return 1; - } - - if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { - FcPatternDestroy(configured); - FcPatternDestroy(match); - return 1; - } - - if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == - XftResultMatch)) { - /* - * Check if xft was unable to find a font with the appropriate - * slant but gave us one anyway. Try to mitigate. - */ - if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) != - XftResultMatch) || - haveattr < wantattr) { - f->badslant = 1; - fputs("font slant does not match\n", stderr); - } - } - - if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == - XftResultMatch)) { - if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != - XftResultMatch) || - haveattr != wantattr) { - f->badweight = 1; - fputs("font weight does not match\n", stderr); - } - } - - XftTextExtentsUtf8(xw.dpy, f->match, (const FcChar8 *)ascii_printable, - strlen(ascii_printable), &extents); - - f->set = NULL; - f->pattern = configured; - - f->ascent = f->match->ascent; - f->descent = f->match->descent; - f->lbearing = 0; - f->rbearing = f->match->max_advance_width; - - f->height = f->ascent + f->descent; - f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); - - return 0; + return SouthEastGravity; } -void xloadfonts(const char *fontstr, double fontsize) { - FcPattern *pattern; - double fontval; +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; - if (fontstr[0] == '-') - pattern = XftXlfdParse(fontstr, False, False); - else - pattern = FcNameParse((const FcChar8 *)fontstr); + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; - if (!pattern) - die("can't open font %s\n", fontstr); + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); - if (fontsize > 1) { - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternDel(pattern, FC_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); - usedfontsize = fontsize; - } else { - if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = fontval; - } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = -1; - } else { - /* - * Default font size is 12, if none given. This is to - * have a known usedfontsize value. - */ - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); - usedfontsize = 12; - } - defaultfontsize = usedfontsize; - } + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); - if (xloadfont(&dc.font, pattern)) - die("can't open font %s\n", fontstr); + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); - if (usedfontsize < 0) { - FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); - usedfontsize = fontval; - if (fontsize == 0) - defaultfontsize = fontval; - } - - /* Setting character width and height. */ - win.cw = ceilf(dc.font.width * cwscale); - win.ch = ceilf(dc.font.height * chscale); - win.cyo = ceilf(dc.font.height * (chscale - 1) / 2); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadfont(&dc.ifont, pattern)) - die("can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_WEIGHT); - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadfont(&dc.ibfont, pattern)) - die("can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadfont(&dc.bfont, pattern)) - die("can't open font %s\n", fontstr); - - FcPatternDestroy(pattern); + return 1; } -int xloadsparefont(FcPattern *pattern, int flags) { - FcPattern *match; - FcResult result; - - match = FcFontMatch(NULL, pattern, &result); - if (!match) { - return 1; - } - - if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { - FcPatternDestroy(match); - return 1; - } - - frc[frclen].flags = flags; - /* Believe U+0000 glyph will present in each default font */ - frc[frclen].unicodep = 0; - frclen++; - - return 0; +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); } -void xloadsparefonts(void) { - FcPattern *pattern; - double sizeshift, fontval; - int fc; - char **fp; - - if (frclen != 0) - die("can't embed spare fonts. cache isn't empty"); - - /* Calculate count of spare fonts */ - fc = sizeof(font2) / sizeof(*font2); - if (fc == 0) - return; - - /* Allocate memory for cache entries. */ - if (frccap < 4 * fc) { - frccap += 4 * fc - frccap; - frc = xrealloc(frc, frccap * sizeof(Fontcache)); - } - - for (fp = font2; fp - font2 < fc; ++fp) { - - if (**fp == '-') - pattern = XftXlfdParse(*fp, False, False); - else - pattern = FcNameParse((FcChar8 *)*fp); - - if (!pattern) - die("can't open spare font %s\n", *fp); - - if (defaultfontsize > 0) { - sizeshift = usedfontsize - defaultfontsize; - if (sizeshift != 0 && FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, - &fontval) == FcResultMatch) { - fontval += sizeshift; - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternDel(pattern, FC_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); - } - } - - FcPatternAddBool(pattern, FC_SCALABLE, 1); - - FcConfigSubstitute(NULL, pattern, FcMatchPattern); - XftDefaultSubstitute(xw.dpy, xw.scr, pattern); - - if (xloadsparefont(pattern, FRC_NORMAL)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadsparefont(pattern, FRC_ITALIC)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_WEIGHT); - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadsparefont(pattern, FRC_ITALICBOLD)) - die("can't open spare font %s\n", *fp); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadsparefont(pattern, FRC_BOLD)) - die("can't open spare font %s\n", *fp); - - FcPatternDestroy(pattern); - } +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); } -void xunloadfont(Font *f) { - XftFontClose(xw.dpy, f->match); - FcPatternDestroy(f->pattern); - if (f->set) - FcFontSetDestroy(f->set); +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; } -void xunloadfonts(void) { - /* Free the loaded fonts in the font cache. */ - while (frclen > 0) - XftFontClose(xw.dpy, frc[--frclen].font); +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; - xunloadfont(&dc.font); - xunloadfont(&dc.bfont); - xunloadfont(&dc.ifont); - xunloadfont(&dc.ibfont); + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + #if USE_XFTFONTMATCH_PATCH + match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); + #else + match = FcFontMatch(NULL, configured, &result); + #endif // USE_XFTFONTMATCH_PATCH + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + #if WIDE_GLYPH_SPACING_PATCH + f->width = DIVCEIL(extents.xOff > 18 ? extents.xOff / 3 : extents.xOff, strlen(ascii_printable)); + #else + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + #endif // WIDE_GLYPH_SPACING_PATCH + + return 0; } -int ximopen(Display *dpy) { - XIMCallback imdestroy = {.client_data = NULL, .callback = ximdestroy}; - XICCallback icdestroy = {.client_data = NULL, .callback = xicdestroy}; +void +xloadfonts(const char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; - xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); - if (xw.ime.xim == NULL) - return 0; + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((const FcChar8 *)fontstr); - if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) - fprintf(stderr, "XSetIMValues: " - "Could not set XNDestroyCallback.\n"); + if (!pattern) + die("can't open font %s\n", fontstr); - xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, NULL); + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } - if (xw.ime.xic == NULL) { - xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, XNClientWindow, - xw.win, XNDestroyCallback, &icdestroy, NULL); - } - if (xw.ime.xic == NULL) - fprintf(stderr, "XCreateIC: Could not create input context.\n"); + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); - return 1; + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + #if VERTCENTER_PATCH + win.cyo = ceilf(dc.font.height * (chscale - 1) / 2); + #endif // VERTCENTER_PATCH + + #if RELATIVEBORDER_PATCH + borderpx = (int) ceilf(((float)borderperc / 100) * win.cw); + #endif // RELATIVEBORDER_PATCH + FcPatternDel(pattern, FC_SLANT); + #if !DISABLE_ITALIC_FONTS_PATCH + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + #endif // DISABLE_ITALIC_FONTS_PATCH + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + #if !DISABLE_BOLD_FONTS_PATCH + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + #endif // DISABLE_BOLD_FONTS_PATCH + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + #if !DISABLE_ROMAN_FONTS_PATCH + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + #endif // DISABLE_ROMAN_FONTS_PATCH + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); } -void ximinstantiate(Display *dpy, XPointer client, XPointer call) { - if (ximopen(dpy)) - XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, - NULL); +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); } -void ximdestroy(XIM xim, XPointer client, XPointer call) { - xw.ime.xim = NULL; - XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, - NULL); - XFree(xw.ime.spotlist); +void +xunloadfonts(void) +{ + #if LIGATURES_PATCH + /* Clear Harfbuzz font cache. */ + hbunloadfonts(); + #endif // LIGATURES_PATCH + + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); } -int xicdestroy(XIC xim, XPointer client, XPointer call) { - xw.ime.xic = NULL; - return 1; +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + #if HIDECURSOR_PATCH + Pixmap blankpm; + #elif !SWAPMOUSE_PATCH + Cursor cursor; + #endif // HIDECURSOR_PATCH + Window parent; + pid_t thispid = getpid(); + #if !SWAPMOUSE_PATCH + XColor xmousefg, xmousebg; + #endif // SWAPMOUSE_PATCH + #if ALPHA_PATCH + XWindowAttributes attr; + XVisualInfo vis; + #endif // ALPHA_PATCH + + #if !XRESOURCES_PATCH + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + #endif // XRESOURCES_PATCH + xw.scr = XDefaultScreen(xw.dpy); + + #if ALPHA_PATCH + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + #else + xw.vis = XDefaultVisual(xw.dpy, xw.scr); + #endif // ALPHA_PATCH + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + #if FONT2_PATCH + /* spare fonts */ + xloadsparefonts(); + #endif // FONT2_PATCH + + /* colors */ + #if ALPHA_PATCH + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + #else + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); + #endif // ALPHA_PATCH + xloadcols(); + + /* adjust fixed window geometry */ + #if ANYSIZE_PATCH + win.w = 2 * win.hborderpx + cols * win.cw; + win.h = 2 * win.vborderpx + rows * win.ch; + #else + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + #endif // ANYSIZE_PATCH + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask + #if ST_EMBEDDER_PATCH + | SubstructureNotifyMask | SubstructureRedirectMask + #endif // ST_EMBEDDER_PATCH + ; + xw.attrs.colormap = xw.cmap; + #if OPENURLONCLICK_PATCH + xw.attrs.event_mask |= PointerMotionMask; + #endif // OPENURLONCLICK_PATCH + + #if !ALPHA_PATCH + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = XRootWindow(xw.dpy, xw.scr); + #endif // ALPHA_PATCH + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + #if ALPHA_PATCH + win.w, win.h, 0, xw.depth, InputOutput, + #else + win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, + #endif // ALPHA_PATCH + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + + #if ALPHA_PATCH + #if SINGLE_DRAWABLE_BUFFER_PATCH + xw.buf = xw.win; + #else + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + #endif // SINGLE_DRAWABLE_BUFFER_PATCH + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + #else + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); + #if SINGLE_DRAWABLE_BUFFER_PATCH + xw.buf = xw.win; + #else + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); + #endif // SINGLE_DRAWABLE_BUFFER_PATCH + #endif // ALPHA_PATCH + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + #if HIDECURSOR_PATCH + xw.pointerisvisible = 1; + #if THEMED_CURSOR_PATCH + xw.vpointer = XcursorLibraryLoadCursor(xw.dpy, mouseshape); + #else + xw.vpointer = XCreateFontCursor(xw.dpy, mouseshape); + #endif // THEMED_CURSOR_PATCH + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + #elif THEMED_CURSOR_PATCH + cursor = XcursorLibraryLoadCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + #else + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + #endif // HIDECURSOR_PATCH + + #if !THEMED_CURSOR_PATCH + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + #endif // THEMED_CURSOR_PATCH + + #if HIDECURSOR_PATCH + #if !THEMED_CURSOR_PATCH + XRecolorCursor(xw.dpy, xw.vpointer, &xmousefg, &xmousebg); + #endif // THEMED_CURSOR_PATCH + blankpm = XCreateBitmapFromData(xw.dpy, xw.win, &(char){0}, 1, 1); + xw.bpointer = XCreatePixmapCursor(xw.dpy, blankpm, blankpm, + &xmousefg, &xmousebg, 0, 0); + #elif !THEMED_CURSOR_PATCH + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + #endif // HIDECURSOR_PATCH + + #if OPENURLONCLICK_PATCH + xw.upointer = XCreateFontCursor(xw.dpy, XC_hand2); + #if !HIDECURSOR_PATCH + xw.vpointer = cursor; + xw.pointerisvisible = 1; + #endif // HIDECURSOR_PATCH + #endif // OPENURLONCLICK_PATCH + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + #if NETWMICON_PATCH + xw.netwmicon = XInternAtom(xw.dpy, "_NET_WM_ICON", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmicon, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&icon, LEN(icon)); + #endif //NETWMICON_PATCH + + #if NO_WINDOW_DECORATIONS_PATCH + Atom motifwmhints = XInternAtom(xw.dpy, "_MOTIF_WM_HINTS", False); + unsigned int data[] = { 0x2, 0x0, 0x0, 0x0, 0x0 }; + XChangeProperty(xw.dpy, xw.win, motifwmhints, motifwmhints, 16, + PropModeReplace, (unsigned char *)data, 5); + #endif // NO_WINDOW_DECORATIONS_PATCH + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + #if FULLSCREEN_PATCH + xw.netwmstate = XInternAtom(xw.dpy, "_NET_WM_STATE", False); + xw.netwmfullscreen = XInternAtom(xw.dpy, "_NET_WM_STATE_FULLSCREEN", False); + #endif // FULLSCREEN_PATCH + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; + + #if BOXDRAW_PATCH + boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + #endif // BOXDRAW_PATCH } -void xinit(int cols, int rows) { - XGCValues gcvalues; - Cursor cursor; - Window parent; - pid_t thispid = getpid(); - XColor xmousefg, xmousebg; - XWindowAttributes attr; - XVisualInfo vis; +#if LIGATURES_PATCH +void +xresetfontsettings(ushort mode, Font **font, int *frcflags) +{ + *font = &dc.font; + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + *font = &dc.ibfont; + *frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + *font = &dc.ifont; + *frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + *font = &dc.bfont; + *frcflags = FRC_BOLD; + } +} +#endif // LIGATURES_PATCH - xw.scr = XDefaultScreen(xw.dpy); +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + #if ANYSIZE_PATCH + float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp; + #else + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + #endif // ANYSIZE_PATCH + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + #if LIGATURES_PATCH + int length = 0, start = 0; + HbTransformData shaped = { 0 }; - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { - parent = XRootWindow(xw.dpy, xw.scr); - xw.depth = 32; - } else { - XGetWindowAttributes(xw.dpy, parent, &attr); - xw.depth = attr.depth; - } + /* Initial values. */ + mode = prevmode = glyphs[0].mode; + xresetfontsettings(mode, &font, &frcflags); + #endif // LIGATURES_PATCH - XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); - xw.vis = vis.visual; + #if VERTCENTER_PATCH + for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) + #else + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) + #endif // VERTCENTER_PATCH + { + /* Fetch rune and mode for current glyph. */ + #if VIM_BROWSE_PATCH + Glyph g = glyphs[i]; + historyOverlay(x+i, y, &g); + rune = g.u; + mode = g.mode; + #elif LIGATURES_PATCH + mode = glyphs[i].mode; + #else + rune = glyphs[i].u; + mode = glyphs[i].mode; + #endif // VIM_BROWSE_PATCH | LIGATURES_PATCH - /* font */ - if (!FcInit()) - die("could not init fontconfig.\n"); + /* Skip dummy wide-character spacing. */ + #if LIGATURES_PATCH + if (mode & ATTR_WDUMMY) + continue; - usedfont = (opt_font == NULL) ? font : opt_font; - xloadfonts(usedfont, 0); + if ( + prevmode != mode + || ATTRCMP(glyphs[start], glyphs[i]) + || selected(x + i, y) != selected(x + start, y) + || i == (len - 1) + ) { + /* Handle 1-character wide segments and end of line */ + length = i - start; + if (i == start) { + length = 1; + } else if (i == (len - 1)) { + length = (i - start + 1); + } - /* spare fonts */ - xloadsparefonts(); + /* Shape the segment. */ + hbtransform(&shaped, font->match, glyphs, start, length); + for (int code_idx = 0; code_idx < shaped.count; code_idx++) { + rune = glyphs[start + code_idx].u; + runewidth = win.cw * ((glyphs[start + code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f); - /* colors */ - xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); - xloadcols(); + if (glyphs[start + code_idx].mode & ATTR_WDUMMY) + continue; - /* adjust fixed window geometry */ - win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw; - win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch; - if (xw.gm & XNegative) - xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; - if (xw.gm & YNegative) - xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + #if BOXDRAW_PATCH + if (glyphs[start + code_idx].mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + specs[numspecs].font = font->match; + specs[numspecs].glyph = boxdrawindex(&glyphs[start + code_idx]); + specs[numspecs].x = xp; + specs[numspecs].y = yp; + xp += runewidth; + numspecs++; + } else + #endif // BOXDRAW_PATCH + if (shaped.glyphs[code_idx].codepoint != 0) { + /* If symbol is found, put it into the specs. */ + specs[numspecs].font = font->match; + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; + specs[numspecs].x = xp + (short)shaped.positions[code_idx].x_offset; + specs[numspecs].y = yp + (short)shaped.positions[code_idx].y_offset; + xp += runewidth; + numspecs++; + } else { + /* If it's not found, try to fetch it through the font cache. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } - /* Events */ - xw.attrs.background_pixel = dc.col[defaultbg].pixel; - xw.attrs.border_pixel = dc.col[defaultbg].pixel; - xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | - ExposureMask | VisibilityChangeMask | - StructureNotifyMask | ButtonMotionMask | - ButtonPressMask | ButtonReleaseMask; - xw.attrs.colormap = xw.cmap; + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); + fcsets[0] = font->set; - xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, win.w, win.h, 0, xw.depth, - InputOutput, xw.vis, - CWBackPixel | CWBorderPixel | CWBitGravity | - CWEventMask | CWColormap, - &xw.attrs); + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); - memset(&gcvalues, 0, sizeof(gcvalues)); - gcvalues.graphics_exposures = False; - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); - XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); - XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - /* font spec buffer */ - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + FcConfigSubstitute(0, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); - /* Xft rendering context */ - xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* input methods */ - if (!ximopen(xw.dpy)) { - XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, - NULL); - } + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } - /* white cursor, black outline */ - cursor = XCreateFontCursor(xw.dpy, mouseshape); - XDefineCursor(xw.dpy, xw.win, cursor); + frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; - if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { - xmousefg.red = 0xffff; - xmousefg.green = 0xffff; - xmousefg.blue = 0xffff; - } + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); - if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { - xmousebg.red = 0x0000; - xmousebg.green = 0x0000; - xmousebg.blue = 0x0000; - } + f = frclen; + frclen++; - XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } - xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); - xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); - xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); - xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); - XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + } - xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); - XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, PropModeReplace, - (uchar *)&thispid, 1); + /* Cleanup and get ready for next segment. */ + hbcleanup(&shaped); + start = i; - win.mode = MODE_NUMLOCK; - resettitle(); - xhints(); - XMapWindow(xw.dpy, xw.win); - XSync(xw.dpy, False); + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + xresetfontsettings(mode, &font, &frcflags); + yp = winy + font->ascent; + } + } + #else // !LIGATURES_PATCH + if (mode == ATTR_WDUMMY) + continue; - clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); - clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); - xsel.primary = NULL; - xsel.clipboard = NULL; - xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); - if (xsel.xtarget == None) - xsel.xtarget = XA_STRING; + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + #if VERTCENTER_PATCH + yp = winy + font->ascent + win.cyo; + #else + yp = winy + font->ascent; + #endif // VERTCENTER_PATCH + } - boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + #if BOXDRAW_PATCH + if (mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + glyphidx = boxdrawindex(&glyphs[i]); + } else { + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + } + #else + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + #endif // BOXDRAW_PATCH + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + #if !USE_XFTFONTMATCH_PATCH + FcConfigSubstitute(0, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + #endif // USE_XFTFONTMATCH_PATCH + + fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + #endif // LIGATURES_PATCH + } + + return numspecs; } -int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, - int x, int y) { - float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, - xp, yp; - ushort mode, prevmode = USHRT_MAX; - Font *font = &dc.font; - int frcflags = FRC_NORMAL; - float runewidth = win.cw; - Rune rune; - FT_UInt glyphidx; - FcResult fcres; - FcPattern *fcpattern, *fontpattern; - FcFontSet *fcsets[] = {NULL}; - FcCharSet *fccharset; - int i, f, numspecs = 0; - for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) { - /* Fetch rune and mode for current glyph. */ - rune = glyphs[i].u; - mode = glyphs[i].mode; +#if UNDERCURL_PATCH +static int isSlopeRising (int x, int iPoint, int waveWidth) +{ + // . . . . + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // . . . . . - /* Skip dummy wide-character spacing. */ - if (mode == ATTR_WDUMMY) - continue; + // Find absolute `x` of point + x += iPoint * (waveWidth/2); - /* Determine font for glyph if different from previous glyph. */ - if (prevmode != mode) { - prevmode = mode; - font = &dc.font; - frcflags = FRC_NORMAL; - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { - font = &dc.ibfont; - frcflags = FRC_ITALICBOLD; - } else if (mode & ATTR_ITALIC) { - font = &dc.ifont; - frcflags = FRC_ITALIC; - } else if (mode & ATTR_BOLD) { - font = &dc.bfont; - frcflags = FRC_BOLD; - } - yp = winy + font->ascent + win.cyo; - } + // Find index of absolute wave + int absSlope = x / ((float)waveWidth/2); - if (mode & ATTR_BOXDRAW) { - /* minor shoehorning: boxdraw uses only this ushort */ - glyphidx = boxdrawindex(&glyphs[i]); - } else { - /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); - } - if (glyphidx) { - specs[numspecs].font = font->match; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - continue; - } - - /* Fallback on font cache, search the font cache for match. */ - for (f = 0; f < frclen; f++) { - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); - /* Everything correct. */ - if (glyphidx && frc[f].flags == frcflags) - break; - /* We got a default font for a not found glyph. */ - if (!glyphidx && frc[f].flags == frcflags && frc[f].unicodep == rune) { - break; - } - } - - /* Nothing was found. Use fontconfig to find matching font. */ - if (f >= frclen) { - if (!font->set) - font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); - fcsets[0] = font->set; - - /* - * Nothing was found in the cache. Now use - * some dozen of Fontconfig calls to get the - * font for one single character. - * - * Xft and fontconfig are design failures. - */ - fcpattern = FcPatternDuplicate(font->pattern); - fccharset = FcCharSetCreate(); - - FcCharSetAddChar(fccharset, rune); - FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - - FcConfigSubstitute(0, fcpattern, FcMatchPattern); - FcDefaultSubstitute(fcpattern); - - fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - - /* Allocate memory for the new cache entry. */ - if (frclen >= frccap) { - frccap += 16; - frc = xrealloc(frc, frccap * sizeof(Fontcache)); - } - - frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); - if (!frc[frclen].font) - die("XftFontOpenPattern failed seeking fallback font: %s\n", - strerror(errno)); - frc[frclen].flags = frcflags; - frc[frclen].unicodep = rune; - - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); - - f = frclen; - frclen++; - - FcPatternDestroy(fcpattern); - FcCharSetDestroy(fccharset); - } - - specs[numspecs].font = frc[f].font; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - } - - return numspecs; + return (absSlope % 2); } -void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, - int x, int y, int dmode) { - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); - int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, - width = charlen * win.cw; - Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; - XRenderColor colfg, colbg; - XRectangle r; +static int getSlope (int x, int iPoint, int waveWidth) +{ + // Sizes: Caps are half width of slopes + // 1_2 1_2 1_2 1_2 + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // 0 3_0 3_0 3_0 3_ + // <2-> <1> <---6----> - /* Fallback on color display for attributes not supported by the font */ - if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { - if (dc.ibfont.badslant || dc.ibfont.badweight) - base.fg = defaultattr; - } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || - (base.mode & ATTR_BOLD && dc.bfont.badweight)) { - base.fg = defaultattr; - } + // Find type of first point + int firstType; + x -= (x / waveWidth) * waveWidth; + if (x < (waveWidth * (2.f/6.f))) + firstType = UNDERCURL_SLOPE_ASCENDING; + else if (x < (waveWidth * (3.f/6.f))) + firstType = UNDERCURL_SLOPE_TOP_CAP; + else if (x < (waveWidth * (5.f/6.f))) + firstType = UNDERCURL_SLOPE_DESCENDING; + else + firstType = UNDERCURL_SLOPE_BOTTOM_CAP; - if (IS_TRUECOL(base.fg)) { - colfg.alpha = 0xffff; - colfg.red = TRUERED(base.fg); - colfg.green = TRUEGREEN(base.fg); - colfg.blue = TRUEBLUE(base.fg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); - fg = &truefg; - } else { - fg = &dc.col[base.fg]; - } + // Find type of given point + int pointType = (iPoint % 4); + pointType += firstType; + pointType %= 4; - if (IS_TRUECOL(base.bg)) { - colbg.alpha = 0xffff; - colbg.green = TRUEGREEN(base.bg); - colbg.red = TRUERED(base.bg); - colbg.blue = TRUEBLUE(base.bg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); - bg = &truebg; - } else { - bg = &dc.col[base.bg]; - } + return pointType; +} +#endif // UNDERCURL_PATCH - /* Change basic system colors [0-7] to bright system colors [8-15] */ - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) - fg = &dc.col[base.fg + 8]; +void +#if WIDE_GLYPHS_PATCH +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int dmode) +#else +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +#endif // WIDE_GLYPHS_PATCH +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + #if ANYSIZE_PATCH + int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch; + #else + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch; + #endif // ANYSIZE_PATCH + int width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; - if (IS_SET(MODE_REVERSE)) { - if (fg == &dc.col[defaultfg]) { - fg = &dc.col[defaultbg]; - } else { - colfg.red = ~fg->color.red; - colfg.green = ~fg->color.green; - colfg.blue = ~fg->color.blue; - colfg.alpha = fg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); - fg = &revfg; - } + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } - if (bg == &dc.col[defaultbg]) { - bg = &dc.col[defaultfg]; - } else { - colbg.red = ~bg->color.red; - colbg.green = ~bg->color.green; - colbg.blue = ~bg->color.blue; - colbg.alpha = bg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &revbg); - bg = &revbg; - } - } + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { - colfg.red = fg->color.red / 2; - colfg.green = fg->color.green / 2; - colfg.blue = fg->color.blue / 2; - colfg.alpha = fg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); - fg = &revfg; - } + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } - if (base.mode & ATTR_REVERSE) { - temp = fg; - fg = bg; - bg = temp; - } + #if !BOLD_IS_NOT_BRIGHT_PATCH + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + #endif // BOLD_IS_NOT_BRIGHT_PATCH - if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) - fg = bg; + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } - if (base.mode & ATTR_INVISIBLE) - fg = bg; + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } - if (dmode & DRAW_BG) { - /* Intelligent cleaning up of the borders. */ - if (x == 0) { - xclear(0, (y == 0) ? 0 : winy, win.hborderpx, - winy + win.ch + - ((winy + win.ch >= win.vborderpx + win.th) ? win.h : 0)); - } - if (winx + width >= win.hborderpx + win.tw) { - xclear(winx + width, (y == 0) ? 0 : winy, win.w, - ((winy + win.ch >= win.vborderpx + win.th) ? win.h - : (winy + win.ch))); - } - if (y == 0) - xclear(winx, 0, winx + width, win.vborderpx); - if (winy + win.ch >= win.vborderpx + win.th) - xclear(winx, winy + win.ch, winx + width, win.h); - XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); - } - /* Set the clip region because Xft is sometimes dirty. */ - // r.x = 0; - // r.y = 0; - // r.height = win.ch; - // r.width = width; - // XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } - if (dmode & DRAW_FG) { - if (base.mode & ATTR_BOXDRAW) { - drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); - } else { - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - } - /* Render underline and strikethrough. */ - if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent + 1, width, - 1); - } + if (base.mode & ATTR_REVERSE) { + #if SPOILER_PATCH + if (bg == fg) { + bg = &dc.col[defaultfg]; + fg = &dc.col[defaultbg]; + } else { + temp = fg; + fg = bg; + bg = temp; + } + #else + temp = fg; + fg = bg; + bg = temp; + #endif // SPOILER_PATCH + } - if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + win.cyo + 2 * dc.font.ascent / 3, - width, 1); - } - } + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; - /* Reset clip to none. */ - // XftDrawSetClip(xw.draw, 0); + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + #if INVERT_PATCH + if (invertcolors) { + revfg = invertedcolor(fg); + revbg = invertedcolor(bg); + fg = &revfg; + bg = &revbg; + } + #endif // INVERT_PATCH + + #if ALPHA_PATCH && ALPHA_GRADIENT_PATCH + // gradient + bg->color.alpha = grad_alpha * 0xffff * (win.h - y*win.ch) / win.h + stat_alpha * 0xffff; + // uncomment to invert the gradient + // bg->color.alpha = grad_alpha * 0xffff * (y*win.ch) / win.h + stat_alpha * 0xffff; + #endif // ALPHA_PATCH | ALPHA_GRADIENT_PATCH + + #if WIDE_GLYPHS_PATCH + if (dmode & DRAW_BG) { + #endif // WIDE_GLYPHS_PATCH + /* Intelligent cleaning up of the borders. */ + #if ANYSIZE_PATCH + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, win.hborderpx, + winy + win.ch + + ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0)); + } + if (winx + width >= win.hborderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, win.vborderpx); + if (winy + win.ch >= win.vborderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + #else + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + #endif // ANYSIZE_PATCH + + /* Clean up the region we want to draw to. */ + #if BACKGROUND_IMAGE_PATCH + if (bg == &dc.col[defaultbg]) + xclear(winx, winy, winx + width, winy + win.ch); + else + #endif // BACKGROUND_IMAGE_PATCH + + #if !WIDE_GLYPHS_PATCH + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + #endif // WIDE_GLYPHS_PATCH + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + #if WIDE_GLYPHS_PATCH + /* Fill the background */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + } + #endif // WIDE_GLYPHS_PATCH + + #if WIDE_GLYPHS_PATCH + if (dmode & DRAW_FG) { + #endif // WIDE_GLYPHS_PATCH + #if BOXDRAW_PATCH + if (base.mode & ATTR_BOXDRAW) { + drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + } else { + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + } + #else + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + #endif // BOXDRAW_PATCH + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + #if UNDERCURL_PATCH + // Underline Color + const int widthThreshold = 28; // +1 width every widthThreshold px of font + int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width + int linecolor; + if ((base.ucolor[0] >= 0) && + !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && + !(base.mode & ATTR_INVISIBLE) + ) { + // Special color for underline + // Index + if (base.ucolor[1] < 0) { + linecolor = dc.col[base.ucolor[0]].pixel; + } + // RGB + else { + XColor lcolor; + lcolor.red = base.ucolor[0] * 257; + lcolor.green = base.ucolor[1] * 257; + lcolor.blue = base.ucolor[2] * 257; + lcolor.flags = DoRed | DoGreen | DoBlue; + XAllocColor(xw.dpy, xw.cmap, &lcolor); + linecolor = lcolor.pixel; + } + } else { + // Foreground color for underline + linecolor = fg->pixel; + } + + XGCValues ugcv = { + .foreground = linecolor, + .line_width = wlw, + .line_style = LineSolid, + .cap_style = CapNotLast + }; + + GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, + &ugcv); + + // Underline Style + if (base.ustyle != 3) { + XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, + winy + dc.font.ascent * chscale + 1, width, wlw); + } else if (base.ustyle == 3) { + int ww = win.cw;//width; + int wh = dc.font.descent - wlw/2 - 1;//r.height/7; + int wx = winx; + int wy = winy + win.ch - dc.font.descent; + #if VERTCENTER_PATCH + wy -= win.cyo; + #endif // VERTCENTER_PATCH + +#if UNDERCURL_STYLE == UNDERCURL_CURLY + // Draw waves + int narcs = charlen * 2 + 1; + XArc *arcs = xmalloc(sizeof(XArc) * narcs); + + int i = 0; + for (i = 0; i < charlen-1; i++) { + arcs[i*2] = (XArc) { + .x = wx + win.cw * i + ww / 4, + .y = wy, + .width = win.cw / 2, + .height = wh, + .angle1 = 0, + .angle2 = 180 * 64 + }; + arcs[i*2+1] = (XArc) { + .x = wx + win.cw * i + ww * 0.75, + .y = wy, + .width = win.cw/2, + .height = wh, + .angle1 = 180 * 64, + .angle2 = 180 * 64 + }; + } + // Last wave + arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, + 0, 180 * 64 }; + // Last wave tail + arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), + wh, 180 * 64, 90 * 64}; + // First wave tail + i++; + arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, + 90 * 64 }; + + XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); + + free(arcs); +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY + // Make the underline corridor larger + /* + wy -= wh; + */ + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + + // Position of wave is independent of word, it's absolute + wx = (wx / (ww/2)) * (ww/2); + + int marginStart = winx - wx; + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + n = (n / ww) * 2; // Number of slopes (/ or \) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Total length of underline + float waveLength = 0; + + if (npoints >= 3) { + // We add an aditional slot in case we use a bonus point + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + points[0] = (XPoint) { + .x = wx + marginStart, + .y = (isSlopeRising(wx, 0, ww)) + ? (wy - marginStart + ww/2.f) + : (wy + marginStart) + }; + + // Second point (Goes back to the absolute point coordinates) + points[1] = (XPoint) { + .x = (ww/2.f) - marginStart, + .y = (isSlopeRising(wx, 1, ww)) + ? (ww/2.f - marginStart) + : (-ww/2.f + marginStart) + }; + waveLength += (ww/2.f) - marginStart; + + // The rest of the points + for (int i = 2; i < npoints-1; i++) { + points[i] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, i, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + } + + // Last point + points[npoints-1] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, npoints-1, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + + // End + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + points[npoints] = (XPoint) { + .x = marginEnd, + .y = (isSlopeRising(wx, npoints, ww)) + ? (marginEnd) + : (-marginEnd) + }; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + if (isSlopeRising(wx, npoints-1, ww)) + points[npoints-1].y -= (marginEnd); + else + points[npoints-1].y += (marginEnd); + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModePrevious); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + points[0].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModePrevious); + } + + // Free resources + free(points); + } +#else // UNDERCURL_CAPPED + // Cap is half of wave width + float capRatio = 0.5f; + + // Make the underline corridor larger + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + ww *= 1 + capRatio; // Add a bit of width for the cap + + // Position of wave is independent of word, it's absolute + wx = (wx / ww) * ww; + + float marginStart; + switch(getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + marginStart = winx - wx; + break; + case UNDERCURL_SLOPE_TOP_CAP: + marginStart = winx - (wx + (ww * (2.f/6.f))); + break; + case UNDERCURL_SLOPE_DESCENDING: + marginStart = winx - (wx + (ww * (3.f/6.f))); + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + marginStart = winx - (wx + (ww * (5.f/6.f))); + break; + } + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + // ._. + n = (n / ww) * 4; // Number of points (./ \.) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Position of the pen to draw the lines + float penX = 0; + float penY = 0; + + if (npoints >= 3) { + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + penX = winx; + switch (getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penY = wy + wh/2.f - marginStart; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY = wy; + break; + case UNDERCURL_SLOPE_DESCENDING: + penY = wy + marginStart; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY = wy + wh/2.f; + break; + } + points[0].x = penX; + points[0].y = penY; + + // Second point (Goes back to the absolute point coordinates) + switch (getSlope(winx, 1, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -wh/2.f + marginStart; + break; + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -marginStart + wh/2.f; + break; + } + points[1].x = penX; + points[1].y = penY; + + // The rest of the points + for (int i = 2; i < npoints; i++) { + switch (getSlope(winx, i, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f); + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f); + penY += -wh / 2.f; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f); + penY += wh / 2.f; + break; + } + points[i].x = penX; + points[i].y = penY; + } + + // End + float waveLength = penX - winx; + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + penX += marginEnd; + switch(getSlope(winx, npoints, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + //penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY += -marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY += marginEnd; + break; + } + + points[npoints].x = penX; + points[npoints].y = penY; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + switch(getSlope(winx, npoints-1, ww)) { + case UNDERCURL_SLOPE_TOP_CAP: + points[npoints-1].y += marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + points[npoints-1].y -= marginEnd; + break; + default: + break; + } + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModeOrigin); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + for (int i = 0; i < npoints; i++) + points[i].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModeOrigin); + } + + // Free resources + free(points); + } +#endif + } + + XFreeGC(xw.dpy, ugc); + #elif VERTCENTER_PATCH + XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent * chscale + 1, + width, 1); + #else + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, + width, 1); + #endif // UNDERCURL_PATCH | VERTCENTER_PATCH + } + + if (base.mode & ATTR_STRUCK) { + #if VERTCENTER_PATCH + XftDrawRect(xw.draw, fg, winx, winy + win.cyo + 2 * dc.font.ascent * chscale / 3, + width, 1); + #else + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, + width, 1); + #endif // VERTCENTER_PATCH + } + #if WIDE_GLYPHS_PATCH + } + #endif // WIDE_GLYPHS_PATCH + + #if OPENURLONCLICK_PATCH + if (url_draw && y >= url_y1 && y <= url_y2) { + int x1 = (y == url_y1) ? url_x1 : 0; + int x2 = (y == url_y2) ? MIN(url_x2, term.col-1) : url_maxcol; + if (x + charlen > x1 && x <= x2) { + int xu = MAX(x, x1); + int wu = (x2 - xu + 1) * win.cw; + #if ANYSIZE_PATCH + xu = win.hborderpx + xu * win.cw; + #else + xu = borderpx + xu * win.cw; + #endif // ANYSIZE_PATCH + #if VERTCENTER_PATCH + XftDrawRect(xw.draw, fg, xu, winy + win.cyo + dc.font.ascent * chscale + 2, wu, 1); + #else + XftDrawRect(xw.draw, fg, xu, winy + dc.font.ascent * chscale + 2, wu, 1); + #endif // VERTCENTER_PATCH + url_draw = (y != url_y2 || x + charlen <= x2); + } + } + #endif // OPENURLONCLICK_PATCH + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); } -void xdrawglyph(Glyph g, int x, int y) { - int numspecs; - XftGlyphFontSpec spec; +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); - xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG); - ; + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + #if WIDE_GLYPHS_PATCH + xdrawglyphfontspecs(&spec, g, numspecs, x, y, DRAW_BG | DRAW_FG); + #else + xdrawglyphfontspecs(&spec, g, numspecs, x, y); + #endif // WIDE_GLYPHS_PATCH } -void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { - Color drawcol; +void +#if LIGATURES_PATCH +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) +#else +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +#endif // LIGATURES_PATCH +{ + Color drawcol; + #if DYNAMIC_CURSOR_COLOR_PATCH + XRenderColor colbg; + #endif // DYNAMIC_CURSOR_COLOR_PATCH - /* remove the old cursor */ - if (selected(ox, oy)) - og.mode ^= ATTR_REVERSE; - xdrawglyph(og, ox, oy); + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + #if LIGATURES_PATCH + /* Redraw the line where cursor was previously. + * It will restore the ligatures broken by the cursor. */ + xdrawline(line, 0, oy, len); + #else + xdrawglyph(og, ox, oy); + #endif // LIGATURES_PATCH - if (IS_SET(MODE_HIDE)) - return; + #if HIDE_TERMINAL_CURSOR_PATCH + if (IS_SET(MODE_HIDE) || !IS_SET(MODE_FOCUSED)) + return; + #else + if (IS_SET(MODE_HIDE)) + return; + #endif // HIDE_TERMINAL_CURSOR_PATCH - /* - * Select the right color for the right mode. - */ - g.mode &= ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK | ATTR_WIDE | - ATTR_BOXDRAW; + /* + * Select the right color for the right mode. + */ + #if BOXDRAW_PATCH + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; + #else + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + #endif // BOXDRAW_PATCH - if (IS_SET(MODE_REVERSE)) { - g.mode |= ATTR_REVERSE; - g.bg = defaultfg; - if (selected(cx, cy)) { - drawcol = dc.col[defaultcs]; - g.fg = defaultrcs; - } else { - drawcol = dc.col[defaultrcs]; - g.fg = defaultcs; - } - } else { - if (selected(cx, cy)) { - g.fg = defaultfg; - g.bg = defaultrcs; - } else { - g.fg = defaultbg; - g.bg = defaultcs; - } - drawcol = dc.col[g.bg]; - } + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } + #if !DYNAMIC_CURSOR_COLOR_PATCH + else { + g.fg = defaultbg; + g.bg = defaultcs; + } - /* draw the new one */ - if (IS_SET(MODE_FOCUSED)) { - switch (win.cursor) { - case 7: /* st extension */ - g.u = 0x2603; /* snowman (U+2603) */ - /* FALLTHROUGH */ - case 0: /* Blinking Block */ - case 1: /* Blinking Block (Default) */ - case 2: /* Steady Block */ - xdrawglyph(g, cx, cy); - break; - case 3: /* Blinking Underline */ - case 4: /* Steady Underline */ - XftDrawRect(xw.draw, &drawcol, win.hborderpx + cx * win.cw, - win.vborderpx + (cy + 1) * win.ch - cursorthickness, win.cw, - cursorthickness); - break; - case 5: /* Blinking bar */ - case 6: /* Steady bar */ - XftDrawRect(xw.draw, &drawcol, win.hborderpx + cx * win.cw, - win.vborderpx + cy * win.ch, cursorthickness, win.ch); - break; - } - } else { - XftDrawRect(xw.draw, &drawcol, win.hborderpx + cx * win.cw, - win.vborderpx + cy * win.ch, win.cw - 1, 1); - XftDrawRect(xw.draw, &drawcol, win.hborderpx + cx * win.cw, - win.vborderpx + cy * win.ch, 1, win.ch - 1); - XftDrawRect(xw.draw, &drawcol, win.hborderpx + (cx + 1) * win.cw - 1, - win.vborderpx + cy * win.ch, 1, win.ch - 1); - XftDrawRect(xw.draw, &drawcol, win.hborderpx + cx * win.cw, - win.vborderpx + (cy + 1) * win.ch - 1, win.cw, 1); - } + drawcol = dc.col[g.bg]; + #else + else if (!(og.mode & ATTR_REVERSE)) { + unsigned int tmpcol = g.bg; + g.bg = g.fg; + g.fg = tmpcol; + } + + if (IS_TRUECOL(g.bg)) { + colbg.alpha = 0xffff; + colbg.red = TRUERED(g.bg); + colbg.green = TRUEGREEN(g.bg); + colbg.blue = TRUEBLUE(g.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol); + } else + drawcol = dc.col[g.bg]; + #endif // DYNAMIC_CURSOR_COLOR_PATCH + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + #if !BLINKING_CURSOR_PATCH + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + #endif // BLINKING_CURSOR_PATCH + case 0: /* Blinking block */ + case 1: /* Blinking block (default) */ + #if BLINKING_CURSOR_PATCH + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + #endif // BLINKING_CURSOR_PATCH + case 2: /* Steady block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking underline */ + #if BLINKING_CURSOR_PATCH + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + #endif // BLINKING_CURSOR_PATCH + case 4: /* Steady underline */ + #if ANYSIZE_PATCH + XftDrawRect(xw.draw, &drawcol, + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + #else + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + #endif // ANYSIZE_PATCH + break; + case 5: /* Blinking bar */ + #if BLINKING_CURSOR_PATCH + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + #endif // BLINKING_CURSOR_PATCH + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + #if ANYSIZE_PATCH + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, + #else + borderpx + cx * win.cw, + borderpx + cy * win.ch, + #endif // ANYSIZE_PATCH + cursorthickness, win.ch); + break; + #if BLINKING_CURSOR_PATCH + case 7: /* Blinking st cursor */ + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + case 8: /* Steady st cursor */ + g.u = stcursor; + xdrawglyph(g, cx, cy); + break; + #endif // BLINKING_CURSOR_PATCH + } + } else { + XftDrawRect(xw.draw, &drawcol, + #if ANYSIZE_PATCH + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, + #else + borderpx + cx * win.cw, + borderpx + cy * win.ch, + #endif // ANYSIZE_PATCH + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + #if ANYSIZE_PATCH + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, + #else + borderpx + cx * win.cw, + borderpx + cy * win.ch, + #endif // ANYSIZE_PATCH + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + #if ANYSIZE_PATCH + win.hborderpx + (cx + 1) * win.cw - 1, + win.vborderpx + cy * win.ch, + #else + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + #endif // ANYSIZE_PATCH + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + #if ANYSIZE_PATCH + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - 1, + #else + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + #endif // ANYSIZE_PATCH + win.cw, 1); + } } -void xsetenv(void) { - char buf[sizeof(long) * 8 + 1]; +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; - snprintf(buf, sizeof(buf), "%lu", xw.win); - setenv("WINDOWID", buf, 1); + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); } -void xseticontitle(char *p) { - XTextProperty prop; - DEFAULT(p, opt_title); +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); - if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != - Success) - return; - XSetWMIconName(xw.dpy, xw.win, &prop); - XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); - XFree(prop.value); + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); } -void xsettitle(char *p) { - XTextProperty prop; - DEFAULT(p, opt_title); +#if CSI_22_23_PATCH +void +xsettitle(char *p, int pop) +{ + XTextProperty prop; - if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop) != - Success) - return; - XSetWMName(xw.dpy, xw.win, &prop); - XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); - XFree(prop.value); + free(titlestack[tstki]); + if (pop) { + titlestack[tstki] = NULL; + tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE; + p = titlestack[tstki] ? titlestack[tstki] : opt_title; + } else if (p) { + titlestack[tstki] = xstrdup(p); + } else { + titlestack[tstki] = NULL; + p = opt_title; + } + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); } -int xstartdraw(void) { return IS_SET(MODE_VISIBLE); } +void +xpushtitle(void) +{ + int tstkin = (tstki + 1) % TITLESTACKSIZE; -void xdrawline(Line line, int x1, int y1, int x2) { - int i, x, ox, numspecs, numspecs_cached; - Glyph base, new; - XftGlyphFontSpec *specs; - - numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); - - /* Draw line in 2 passes: background and foreground. This way wide glyphs - won't get truncated (#223) */ - for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { - specs = xw.specbuf; - numspecs = numspecs_cached; - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = line[x]; - if (new.mode == ATTR_WDUMMY) - continue; - if (selected(x, y1)) - new.mode ^= ATTR_REVERSE; - if (i > 0 && ATTRCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; - } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); - } + free(titlestack[tstkin]); + titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL; + tstki = tstkin; } -void xfinishdraw(void) { - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); - XSetForeground(xw.dpy, dc.gc, - dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); +void +xfreetitlestack(void) +{ + for (int i = 0; i < LEN(titlestack); i++) { + free(titlestack[i]); + titlestack[i] = NULL; + } +} +#else +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} +#endif // CSI_22_23_PATCH + +int +xstartdraw(void) +{ + #if W3M_PATCH + if (IS_SET(MODE_VISIBLE)) + XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); + #endif // W3M_PATCH + return IS_SET(MODE_VISIBLE); } -void xximspot(int x, int y) { - if (xw.ime.xic == NULL) - return; +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + #if WIDE_GLYPHS_PATCH + int numspecs_cached; + #endif // WIDE_GLYPHS_PATCH + Glyph base, new; + #if WIDE_GLYPHS_PATCH + XftGlyphFontSpec *specs; - xw.ime.spot.x = borderpx + x * win.cw; - xw.ime.spot.y = borderpx + (y + 1) * win.ch; + numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); - XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); + /* Draw line in 2 passes: background and foreground. This way wide glyphs + won't get truncated (#223) */ + for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { + specs = xw.specbuf; + numspecs = numspecs_cached; + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + #if VIM_BROWSE_PATCH + historyOverlay(x, y1, &new); + #endif // VIM_BROWSE_PATCH + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); + } + #else + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + #if VIM_BROWSE_PATCH + historyOverlay(x, y1, &new); + #endif // VIM_BROWSE_PATCH + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); + #endif // WIDE_GLYPHS_PATCH } -void expose(XEvent *ev) { redraw(); } +void +xfinishdraw(void) +{ + #if SIXEL_PATCH + ImageList *im, *next; + XGCValues gcvalues; + GC gc; + #endif // SIXEL_PATCH -void visibility(XEvent *ev) { - XVisibilityEvent *e = &ev->xvisibility; + #if SIXEL_PATCH + for (im = term.images; im; im = next) { + /* get the next image here, because delete_image() will delete the current image */ + next = im->next; - MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); + if (im->should_delete) { + delete_image(im); + continue; + } + + if (!im->pixmap) { + im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, im->width, im->height, + #if ALPHA_PATCH + xw.depth + #else + DefaultDepth(xw.dpy, xw.scr) + #endif // ALPHA_PATCH + ); + XImage ximage = { + .format = ZPixmap, + .data = (char *)im->pixels, + .width = im->width, + .height = im->height, + .xoffset = 0, + .byte_order = LSBFirst, + .bitmap_bit_order = MSBFirst, + .bits_per_pixel = 32, + .bytes_per_line = im->width * 4, + .bitmap_unit = 32, + .bitmap_pad = 32, + #if ALPHA_PATCH + .depth = xw.depth + #else + .depth = 24 + #endif // ALPHA_PATCH + }; + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, im->width, im->height); + free(im->pixels); + im->pixels = NULL; + } + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues); + + #if ANYSIZE_PATCH + XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, im->width, im->height, win.hborderpx + im->x * win.cw, win.vborderpx + im->y * win.ch); + #else + XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, im->width, im->height, borderpx + im->x * win.cw, borderpx + im->y * win.ch); + #endif // ANYSIZE_PATCH + XFreeGC(xw.dpy, gc); + + } + #endif // SIXEL_PATCH + + #if !SINGLE_DRAWABLE_BUFFER_PATCH + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); + #endif // SINGLE_DRAWABLE_BUFFER_PATCH + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); } -void unmap(XEvent *ev) { win.mode &= ~MODE_VISIBLE; } +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; -void xsetpointermotion(int set) { - MODBIT(xw.attrs.event_mask, set, PointerMotionMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); } -void xsetmode(int set, unsigned int flags) { - int mode = win.mode; - MODBIT(win.mode, set, flags); - if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) - redraw(); +void +expose(XEvent *ev) +{ + redraw(); } -int xsetcursor(int cursor) { - if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ - return 1; - win.cursor = cursor; - return 0; +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); } -void xseturgency(int add) { - XWMHints *h = XGetWMHints(xw.dpy, xw.win); - - MODBIT(h->flags, add, XUrgencyHint); - XSetWMHints(xw.dpy, xw.win, h); - XFree(h); +void +unmap(XEvent *ev) +{ + #if ST_EMBEDDER_PATCH + if (embed == ev->xunmap.window) { + embed = 0; + XRaiseWindow(xw.dpy, xw.win); + XSetInputFocus(xw.dpy, xw.win, RevertToParent, CurrentTime); + } + #endif // ST_EMBEDDER_PATCH + win.mode &= ~MODE_VISIBLE; } -void xbell(void) { - if (!(IS_SET(MODE_FOCUSED))) - xseturgency(1); - if (bellvolume) - XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +void +xsetpointermotion(int set) +{ + #if HIDECURSOR_PATCH + if (!set && !xw.pointerisvisible) + return; + #endif // HIDECURSOR_PATCH + #if OPENURLONCLICK_PATCH + set = 1; /* keep MotionNotify event enabled */ + #endif // OPENURLONCLICK_PATCH + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } -void focus(XEvent *ev) { - XFocusChangeEvent *e = &ev->xfocus; - - if (e->mode == NotifyGrab) - return; - - if (ev->type == FocusIn) { - if (xw.ime.xic) - XSetICFocus(xw.ime.xic); - win.mode |= MODE_FOCUSED; - xseturgency(0); - if (IS_SET(MODE_FOCUS)) - ttywrite("\033[I", 3, 0); - } else { - if (xw.ime.xic) - XUnsetICFocus(xw.ime.xic); - win.mode &= ~MODE_FOCUSED; - if (IS_SET(MODE_FOCUS)) - ttywrite("\033[O", 3, 0); - } +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + #if SWAPMOUSE_PATCH + if ((flags & MODE_MOUSE) + #if HIDECURSOR_PATCH + && xw.pointerisvisible + #endif // HIDECURSOR_PATCH + ) { + if (win.mode & MODE_MOUSE) + XUndefineCursor(xw.dpy, xw.win); + else + #if HIDECURSOR_PATCH + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + #else + XDefineCursor(xw.dpy, xw.win, cursor); + #endif // HIDECURSOR_PATCH + } + #elif OPENURLONCLICK_PATCH + if (win.mode & MODE_MOUSE && xw.pointerisvisible) + XDefineCursor(xw.dpy, xw.win, xw.vpointer); + #endif // SWAPMOUSE_PATCH + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); } -int match(uint mask, uint state) { - return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +int +xsetcursor(int cursor) +{ + #if BLINKING_CURSOR_PATCH + if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */ + #else + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + #endif // BLINKING_CURSOR_PATCH + return 1; + #if DEFAULT_CURSOR_PATCH + #if BLINKING_CURSOR_PATCH + win.cursor = (cursor ? cursor : cursorstyle); + #else + win.cursor = (cursor ? cursor : cursorshape); + #endif // BLINKING_CURSOR_PATCH + #else + win.cursor = cursor; + #endif // DEFAULT_CURSOR_PATCH + #if BLINKING_CURSOR_PATCH + cursorblinks = win.cursor == 0 || win.cursor == 1 || + win.cursor == 3 || win.cursor == 5 || + win.cursor == 7; + #endif // BLINKING_CURSOR_PATCH + return 0; } -char *kmap(KeySym k, uint state) { - Key *kp; - int i; +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); - /* Check for mapped keys out of X11 function keys. */ - for (i = 0; i < LEN(mappedkeys); i++) { - if (mappedkeys[i] == k) - break; - } - if (i == LEN(mappedkeys)) { - if ((k & 0xFFFF) < 0xFD00) - return NULL; - } - - for (kp = key; kp < key + LEN(key); kp++) { - if (kp->k != k) - continue; - - if (!match(kp->mask, state)) - continue; - - if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) - continue; - if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) - continue; - - if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) - continue; - - return kp->s; - } - - return NULL; + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); } -void kpress(XEvent *ev) { - XKeyEvent *e = &ev->xkey; - KeySym ksym; - char buf[64], *customkey; - int len; - Rune c; - Status status; - Shortcut *bp; - - if (IS_SET(MODE_KBDLOCK)) - return; - - if (xw.ime.xic) - len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); - else - len = XLookupString(e, buf, sizeof buf, &ksym, NULL); - /* 1. shortcuts */ - for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { - if (ksym == bp->keysym && match(bp->mod, e->state)) { - bp->func(&(bp->arg)); - return; - } - } - - /* 2. custom keys from config.h */ - if ((customkey = kmap(ksym, e->state))) { - ttywrite(customkey, strlen(customkey), 1); - return; - } - - /* 3. composed string from input method */ - if (len == 0) - return; - if (len == 1 && e->state & Mod1Mask) { - if (IS_SET(MODE_8BIT)) { - if (*buf < 0177) { - c = *buf | 0x80; - len = utf8encode(c, buf); - } - } else { - buf[1] = buf[0]; - buf[0] = '\033'; - len = 2; - } - } - ttywrite(buf, len, 1); +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); + #if VISUALBELL_1_PATCH + if (!bellon) /* turn visual bell on */ + bellon = 1; + #endif // VISUALBELL_1_PATCH } -void cmessage(XEvent *e) { - /* - * See xembed specs - * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html - */ - if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { - if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { - win.mode |= MODE_FOCUSED; - xseturgency(0); - } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { - win.mode &= ~MODE_FOCUSED; - } - } else if (e->xclient.data.l[0] == xw.wmdeletewin) { - ttyhangup(); - exit(0); - } +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + #if ST_EMBEDDER_PATCH + if (embed && ev->type == FocusIn) { + XRaiseWindow(xw.dpy, embed); + XSetInputFocus(xw.dpy, embed, RevertToParent, CurrentTime); + sendxembed(XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); + sendxembed(XEMBED_WINDOW_ACTIVATE, 0, 0, 0); + } + #endif // ST_EMBEDDER_PATCH + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH + if (!focused) { + focused = 1; + xloadcols(); + tfulldirt(); + } + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH + if (focused) { + focused = 0; + xloadcols(); + tfulldirt(); + } + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + } } -void resize(XEvent *e) { - if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) - return; - - cresize(e->xconfigure.width, e->xconfigure.height); +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); } -void run(void) { - XEvent ev; - int w = win.w, h = win.h; - fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; - struct timespec seltv, *tv, now, lastblink, trigger; - double timeout; +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; - /* Waiting for window mapping */ - do { - XNextEvent(xw.dpy, &ev); - /* - * This XFilterEvent call is required because of XOpenIM. It - * does filter out the key event and some client message for - * the input method too. - */ - if (XFilterEvent(&ev, None)) - continue; - if (ev.type == ConfigureNotify) { - w = ev.xconfigure.width; - h = ev.xconfigure.height; - } - } while (ev.type != MapNotify); + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } - ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); - cresize(w, h); + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; - for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { - FD_ZERO(&rfd); - FD_SET(ttyfd, &rfd); - FD_SET(xfd, &rfd); + if (!match(kp->mask, state)) + continue; - if (XPending(xw.dpy)) - timeout = 0; /* existing events might not set xfd */ + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; - seltv.tv_sec = timeout / 1E3; - seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); - tv = timeout >= 0 ? &seltv : NULL; + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; - if (pselect(MAX(xfd, ttyfd) + 1, &rfd, NULL, NULL, tv, NULL) < 0) { - if (errno == EINTR) - continue; - die("select failed: %s\n", strerror(errno)); - } - clock_gettime(CLOCK_MONOTONIC, &now); + return kp->s; + } - if (FD_ISSET(ttyfd, &rfd)) - ttyread(); - - xev = 0; - while (XPending(xw.dpy)) { - xev = 1; - XNextEvent(xw.dpy, &ev); - if (XFilterEvent(&ev, None)) - continue; - if (handler[ev.type]) - (handler[ev.type])(&ev); - } - - /* - * To reduce flicker and tearing, when new content or event - * triggers drawing, we first wait a bit to ensure we got - * everything, and if nothing new arrives - we draw. - * We start with trying to wait minlatency ms. If more content - * arrives sooner, we retry with shorter and shorter periods, - * and eventually draw even without idle after maxlatency ms. - * Typically this results in low latency while interacting, - * maximum latency intervals during `cat huge.txt`, and perfect - * sync with periodic updates from animations/key-repeats/etc. - */ - if (FD_ISSET(ttyfd, &rfd) || xev) { - if (!drawing) { - trigger = now; - drawing = 1; - } - timeout = (maxlatency - TIMEDIFF(now, trigger)) / maxlatency * minlatency; - if (timeout > 0) - continue; /* we have time, try to find idle */ - } - - /* idle detected or maxlatency exhausted -> draw */ - timeout = -1; - if (blinktimeout && tattrset(ATTR_BLINK)) { - timeout = blinktimeout - TIMEDIFF(now, lastblink); - if (timeout <= 0) { - if (-timeout > blinktimeout) /* start visible */ - win.mode |= MODE_BLINK; - win.mode ^= MODE_BLINK; - tsetdirtattr(ATTR_BLINK); - lastblink = now; - timeout = blinktimeout; - } - } - - draw(); - XFlush(xw.dpy); - drawing = 0; - } + return NULL; } -int resource_load(XrmDatabase db, char *name, enum resource_type rtype, - void *dst) { - char **sdst = dst; - int *idst = dst; - float *fdst = dst; +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym = NoSymbol; + char buf[64], *customkey; + int len, screen; + Rune c; + Status status; + Shortcut *bp; - char fullname[256]; - char fullclass[256]; - char *type; - XrmValue ret; + #if HIDECURSOR_PATCH + if (xw.pointerisvisible) { + #if OPENURLONCLICK_PATCH + #if ANYSIZE_PATCH + int x = e->x - win.hborderpx; + int y = e->y - win.vborderpx; + #else + int x = e->x - borderpx; + int y = e->y - borderpx; + #endif // ANYSIZE_PATCH + LIMIT(x, 0, win.tw - 1); + LIMIT(y, 0, win.th - 1); + if (!detecturl(x / win.cw, y / win.ch, 0)) { + XDefineCursor(xw.dpy, xw.win, xw.bpointer); + xsetpointermotion(1); + xw.pointerisvisible = 0; + } + #else + XDefineCursor(xw.dpy, xw.win, xw.bpointer); + xsetpointermotion(1); + xw.pointerisvisible = 0; + #endif // OPENURLONCLICK_PATCH + } + #endif // HIDECURSOR_PATCH - snprintf(fullname, sizeof(fullname), "%s.%s", opt_name ? opt_name : "st", - name); - snprintf(fullclass, sizeof(fullclass), "%s.%s", opt_class ? opt_class : "St", - name); - fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; + if (IS_SET(MODE_KBDLOCK)) + return; - XrmGetResource(db, fullname, fullclass, &type, &ret); - if (ret.addr == NULL || strncmp("String", type, 64)) - return 1; + if (xw.ime.xic) { + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + if (status == XBufferOverflow) + return; + } else { + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + } + #if KEYBOARDSELECT_PATCH + if ( IS_SET(MODE_KBDSELECT) ) { + if ( match(XK_NO_MOD, e->state) || + (XK_Shift_L | XK_Shift_R) & e->state ) + win.mode ^= trt_kbdselect(ksym, buf, len); + return; + } + #endif // KEYBOARDSELECT_PATCH + #if VIM_BROWSE_PATCH + if (IS_SET(MODE_NORMAL)) { + if (kPressHist(buf, len, match(ControlMask, e->state), &ksym) + == finish) normalMode(); + return; + } + #endif // VIM_BROWSE_PATCH - switch (rtype) { - case STRING: - *sdst = ret.addr; - break; - case INTEGER: - *idst = strtoul(ret.addr, NULL, 10); - break; - case FLOAT: - *fdst = strtof(ret.addr, NULL); - break; - } - return 0; + screen = tisaltscr() ? S_ALT : S_PRI; + + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state) && + (!bp->screen || bp->screen == screen)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); } -#define XRESOURCE_LOAD_META(NAME) \ - if (!XrmGetResource(xrdb, "st." NAME, "st." NAME, &type, &ret)) \ - XrmGetResource(xrdb, "*." NAME, "*." NAME, &type, &ret); \ - if (ret.addr != NULL && !strncmp("String", type, 64)) -#define XRESOURCE_LOAD_STRING(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = ret.addr; - -#define XRESOURCE_LOAD_CHAR(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = ret.addr[0]; - -#define XRESOURCE_LOAD_INTEGER(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = strtoul(ret.addr, NULL, 10); - -#define XRESOURCE_LOAD_FLOAT(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = strtof(ret.addr, NULL); - -void xrdb_load(void) { - /* XXX */ - char *xrm; - char *type; - XrmDatabase xrdb; - XrmValue ret; - Display *dpy; - - if (!(dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); - - XrmInitialize(); - xrm = XResourceManagerString(dpy); - - if (xrm != NULL) { - xrdb = XrmGetStringDatabase(xrm); - - /* handling colors here without macros to do via loop. */ - int i = 0; - char loadValue[12] = ""; - for (i = 0; i < 256; i++) { - sprintf(loadValue, "%s%d", "st.color", i); - - if (!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret)) { - sprintf(loadValue, "%s%d", "*.color", i); - if (!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret)) - /* reset if not found (unless in range for defaults). */ - if (i > 15) - colorname[i] = NULL; - } - - if (ret.addr != NULL && !strncmp("String", type, 64)) - colorname[i] = ret.addr; - } - - XRESOURCE_LOAD_STRING("foreground", colorname[defaultfg]); - XRESOURCE_LOAD_STRING("background", colorname[defaultbg]); - XRESOURCE_LOAD_STRING("cursorColor", colorname[defaultcs]) - else { - // this looks confusing because we are chaining off of the if - // in the macro. probably we should be wrapping everything blocks - // so this isn't possible... - defaultcs = defaultfg; - } - XRESOURCE_LOAD_STRING("reverse-cursor", colorname[defaultrcs]) - else { - // see above. - defaultrcs = defaultbg; - } - - XRESOURCE_LOAD_STRING("font", font); - XRESOURCE_LOAD_STRING("termname", termname); - - XRESOURCE_LOAD_INTEGER("blinktimeout", blinktimeout); - XRESOURCE_LOAD_INTEGER("bellvolume", bellvolume); - XRESOURCE_LOAD_INTEGER("borderpx", borderpx); - XRESOURCE_LOAD_INTEGER("cursorshape", cursorshape); - - XRESOURCE_LOAD_FLOAT("cwscale", cwscale); - XRESOURCE_LOAD_FLOAT("chscale", chscale); - } - XFlush(dpy); -} -void usage(void) { - die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid]" - " [[-e] command [args ...]]\n" - " %s [-aiv] [-c class] [-f font] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid] -l line" - " [stty_args ...]\n", - argv0, argv0); +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } } -void reload(int sig) { - xrdb_load(); - /* colors, fonts */ - xloadcols(); - xunloadfonts(); - xloadfonts(font, 0); +void +resize(XEvent *e) +{ + #if ST_EMBEDDER_PATCH + XWindowChanges wc; + #endif // ST_EMBEDDER_PATCH - /* pretend the window just got resized */ - cresize(win.w, win.h); - ttyresize(win.tw, win.th); + #if BACKGROUND_IMAGE_PATCH + if (pseudotransparency) { + if (e->xconfigure.width == win.w && + e->xconfigure.height == win.h && + e->xconfigure.x == win.x && e->xconfigure.y == win.y) + return; + updatexy(); + } else + #endif // BACKGROUND_IMAGE_PATCH + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; - redraw(); + #if ST_EMBEDDER_PATCH + if (embed) { + wc.width = e->xconfigure.width; + wc.height = e->xconfigure.height; + XConfigureWindow(xw.dpy, embed, CWWidth | CWHeight, &wc); + } + #endif // ST_EMBEDDER_PATCH - /* triggers re-render if we're visible. */ - ttywrite("\033[O", 3, 0); - - signal(SIGUSR1, reload); + cresize(e->xconfigure.width, e->xconfigure.height); } -int main(int argc, char *argv[]) { - xw.l = xw.t = 0; - xw.isfixed = False; - xsetcursor(cursorshape); +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; - ARGBEGIN { - case 'a': - allowaltscreen = 0; - break; - case 'A': - opt_alpha = EARGF(usage()); - break; - case 'c': - opt_class = EARGF(usage()); - break; - case 'e': - if (argc > 0) - --argc, ++argv; - goto run; - case 'f': - opt_font = EARGF(usage()); - break; - case 'g': - xw.gm = XParseGeometry(EARGF(usage()), &xw.l, &xw.t, &cols, &rows); - break; - case 'i': - xw.isfixed = 1; - break; - case 'o': - opt_io = EARGF(usage()); - break; - case 'l': - opt_line = EARGF(usage()); - break; - case 'n': - opt_name = EARGF(usage()); - break; - case 't': - case 'T': - opt_title = EARGF(usage()); - break; - case 'w': - opt_embed = EARGF(usage()); - break; - case 'v': - die("%s " VERSION "\n", argv0); - break; - default: - usage(); - } - ARGEND; + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + #if SYNC_PATCH + if (XPending(xw.dpy) || ttyread_pending()) + #else + if (XPending(xw.dpy)) + #endif // SYNC_PATCH + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + #if SYNC_PATCH + int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); + if (ttyin) + ttyread(); + #else + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + #endif // SYNC_PATCH + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + #if SYNC_PATCH + if (ttyin || xev) + #else + if (FD_ISSET(ttyfd, &rfd) || xev) + #endif // SYNC_PATCH + { + if (!drawing) { + trigger = now; + #if BLINKING_CURSOR_PATCH + if (IS_SET(MODE_BLINK)) { + win.mode ^= MODE_BLINK; + } + lastblink = now; + #endif // BLINKING_CURSOR_PATCH + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + #if SYNC_PATCH + if (tinsync(su_timeout)) { + /* + * on synchronized-update draw-suspension: don't reset + * drawing so that we draw ASAP once we can (just after + * ESU). it won't be too soon because we already can + * draw now but we skip. we set timeout > 0 to draw on + * SU-timeout even without new content. + */ + timeout = minlatency; + continue; + } + #endif // SYNC_PATCH + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + #if BLINKING_CURSOR_PATCH + if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) + #else + if (blinktimeout && tattrset(ATTR_BLINK)) + #endif // BLINKING_CURSOR_PATCH + { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; + } + } + + #if VISUALBELL_1_PATCH + if (bellon) { + bellon++; + bellon %= 3; + MODBIT(win.mode, !IS_SET(MODE_REVERSE), MODE_REVERSE); + redraw(); + } + else + draw(); + #else + draw(); + #endif // VISUALBELL_1_PATCH + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class]" + #if WORKINGDIR_PATCH + " [-d path]" + #endif // WORKINGDIR_PATCH + " [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class]" + #if WORKINGDIR_PATCH + " [-d path]" + #endif // WORKINGDIR_PATCH + " [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + #if BLINKING_CURSOR_PATCH + xsetcursor(cursorstyle); + #else + xsetcursor(cursorshape); + #endif // BLINKING_CURSOR_PATCH + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + #if ALPHA_PATCH + case 'A': + opt_alpha = EARGF(usage()); + break; + #endif // ALPHA_PATCH + case 'c': + opt_class = EARGF(usage()); + break; + #if WORKINGDIR_PATCH + case 'd': + opt_dir = EARGF(usage()); + break; + #endif // WORKINGDIR_PATCH + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; run: - if (argc > 0) /* eat all remaining arguments */ - opt_cmd = argv; + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; - if (!opt_title) - opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; - setlocale(LC_CTYPE, ""); - XSetLocaleModifiers(""); - if (!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH + signal(SIGUSR1, sigusr1_reload); + #endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH + #if XRESOURCES_PATCH + if (!(xw.dpy = XOpenDisplay(NULL))) + die("Can't open display\n"); - xrdb_load(); - cols = MAX(cols, 1); - rows = MAX(rows, 1); - signal(SIGUSR1, reload); - tnew(cols, rows); - xinit(cols, rows); - xsetenv(); - selinit(); - run(); + config_init(xw.dpy); + #endif // XRESOURCES_PATCH + cols = MAX(cols, 1); + rows = MAX(rows, 1); + #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH + defaultbg = MAX(LEN(colorname), 256); + #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH + tnew(cols, rows); + xinit(cols, rows); + #if BACKGROUND_IMAGE_PATCH + bginit(); + #endif // BACKGROUND_IMAGE_PATCH + xsetenv(); + selinit(); + #if WORKINGDIR_PATCH + if (opt_dir && chdir(opt_dir)) + die("Can't change to working directory %s\n", opt_dir); + #endif // WORKINGDIR_PATCH + run(); - return 0; + return 0; } diff --git a/x.o b/x.o index b1e50b478c5b50ed78410ea591b0600571c3b811..8448d7061afc11ce70753181d87efed9ce7a7039 100644 GIT binary patch literal 118848 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�)FXJE@-gEl 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 literal 90056 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
    ?QetAH30wMkN-T7d`Em< z^4;;%lOKf0w)h#zTM%!J$6#?=JbK^vcmV<6?S|)N>hBKT4kPf;`27d5aR?$Z%*5#qoO7OqaYeCA ziDzCvJXD?r!=rwjf$POMt}$5N5AlcV0&$n4?dGYrdtD&@+<>_{4~;FqFF#S^>}Mtq z5BZUgs9FupNKT)ToY4R`H_b)0fni3uGU6@7m^e2%k@~$%gP%n>WWeM!ysI>YhBk(1 zrFwj)^5W;bDjcOF)mASv*fhc*XTFh>gUcHY?firK=8WX+331(n)JzX!T+BtogQ@P{ zG&0U2Kku*ZVZzG?9K3yvqRAD)O8NtR<1W&3$B4l%H+_8{W2x%=tpKj8cAi=rKP)3=U=IzAKeCY;1+Tf~R#5v=~ z8GM1^<9R>PNq)#=&GYP24L|iI6i(EUGZ8W6+iI>8O-y4w;WwCTUK^0_5dL?AtM!37 zkMFg@)Zi>c0hGGU=(FAMQWWo)4pCtAgI$t;nc-ha;X&Qv4k zBO_;-;?sZP={r$YD8V)5JDd6fgftyF6)HZEzX~|<*BO4b{s49pDv)ZCz6?Pj)g-+b z=foc*pG8n>62xn8P`Pr9oPD^a(1?Th7aM-HP60-JFY&9f8s~=W{MO(%BBsC^|6W}U zbWGD!SM=vk20uK8=#x7Uk^Jin&hqb7*NHK|GGz6VKGO3N)6c2(4~S3WAbe6#bFMWB zUNQKC22VEl+Xmlc@FIinH2Cq5YzZvL^j3N5uaNty~Eyi$aZ3EKr2LH<7YFz{TRD&;7lLWJ_0e-r{N6$BJJcNznqqEaD8^e-gmh!Zs68vJmBKO543TI1pLM1wCd{Azs&d?awv|2~RDI8EV+ zv3(ULO)&f$7%=fVeeRKaKp)E&LHB z-*obzh2Uot)*CtL`ZD(SNrR`x5PkBTk`wPV8Jzw0P2g0IFaB5)N)7!3gJ+oxJSEq0 zK2`BRC&B1p*qyHoPA^20;ah{BY~cabeg-@JKtdB)R>I9z0_d-M)e-a)C9HO z#_6qwpXVja|7U|2T5|rT@WD>aNt(d?_W&n7FSqnm-|Yt8Y~ku1gTQ}n;Tw#cKUnxv z2LGppZ#8(kg{yTsheSK_)Sw(BQ=u|2YP)w(x}pUu)q@4E`S$zRcjiv+yc| ztB*^tGKixFf5qaz#NfLve2u{mHU7Y&t}^(^7JiMvbw9`4j`JS|*W+Oiext$vX65@^ zgKx3$+YL_Rj0}G<_k(e-Z;G{?725;Uz|mqm3BHUge_SQuvVA{E!nQ8$5>r6Ax8* zirRCNB)u+AQTuBWr~4QOmJ0i~KKN)KoW3pGmz)A0e31{n4EPYtGkHG7{dScPf2|MR zwNH^`QX3w!S4Y+1mkAbNxFl>r3#Pu z@c+vPf71v5#0US{2R{Tud!O~#mw_m@fkoxoY{@?gcwhBv>vF^{fR>pZ4K@ z%?JO;2jB05AAu!(U**NinU$Z6n5p$q&uZZGBp?0^!~dU#Up>PC{t3Xz&#%^EV*PEz zN6wi(_>cnGe3g2ao#Tm-*oIg}T1%bAu252OpfiUfY+P2Yv8$KKQf1$)0O0d%mjh z0nV2e-eK^47XFpNzq9b~6`o+wo_*bV%%vMQ{q(BufQh*mOFTnvQxGauX4uUT6}(>p#tBoP~R$W8df zm+DtVHRiveQdA~SEi3nC-jL@`RmrxnXk0TLdGt^(DbhbL3;NTM=#r2g@RY3Z_1WcxK zP~VxLG&<#Y2d_7#mn7@VR6fOZB^4EmYVhrYsQRo0aZglYC-rdEIsATqkjLX&82Y{b zxEMEqc*iOE@+S* zA*y`imDus9UV3?Qol{*CU8KHuKqu;*-_9k~)hmjZ)WyoJ04Ra)br8v^t*=taN>-Gr zxT2cGj~}lt=oQ*@IacL4UKw`$L`T1Px>hl&LXIEj^!kWG$%?W%dJQ>!GXtONz>B68 zuhKbF>6|&1kj!!T@EtG_DGOt@fmNxgEGOl6DAN!qQ**O#n7 z7Wjb2l3Kjnx){y}5HG2Q3F*z)`fLfla8Xz1EH_cv#SS@9S?L&6Jrk5R6Eakxiz{oE z6{|D#vTt>!`~eD2P{o~~RG6TuV}dfx1XZ+&s%R53+39N0o~S0&%hRbecsacIo&*6Z z`-!TM6IFpHsvPh(c%mxV6IE^#RSuI>4wF=>Nh;MOrTQe5c9KeqH_;>QBqd>zl8~iR zWvNtIDpi)^$x_s8Ma|YSRW4adW|oqfrDSF)X<6`J^+^u>r3>VetvIt4XZ8dKT~{4t zo~^`Zt3=rMfr!7^SE$14po^lDp60D=$beh%+`Uzy>QR+Q13-_`rb!}OV!S@c^Wx|m*K zUN9f2uc@pk!TZde=_R=!qX7D4?>*Py(o&)TFlh_`)?V1OYK#$(qCNyOw|+?-d<)-e z0Tj_4T`z1pdPYdVtJF)Dk#2M?UVPsp8Lw5R595$ztzQqIWKh@LfOpZ8 zML5@A&Ourr_+$$W4X_73A7gAag9aBR0fUQETSg43w&u@tRgXoL89Sd&RbE%6dfamA zkXO}Kpln9*+)5f)rk9vP_0}*}#G=ab(n9(kky4=#n{b_=}YuwUZal>$kaOJx@a(*8V-u6 z=!F#+km2jvH~30B9jc`Y{V8n1DdvT zA(}Tvfp1oo8lAPU7^&R&C~O85@^h!F(TSXyFE?WGP1feU35e7~rt>!M)_eGOVx z(TXJn)z!=EYv$??Efw{;s)yTP9#vh6?vMtwlIog^)%=R2^1M#9IMutUMg&JvO%=Ww ztu7}OYARqp?jtY@fyXK$6q{3B&7Zw0QtjxRin{vZ%KWPGYIt=%K0d`>qB0OZstv|e ztaQl&(|f-BL{(i${Uz!JjmLBfrtdR;PR%)2jVek%%sBW;HA0mobSx{OPf0Kd+037b z+K5&Iu0zLyTF|2`N>IaU&Aqs)g!|`34fGW)-S?=kmI#+KnR*CQTP z7Rpgp$~`}ZcADc>&__Aw+bB*E)f2ULYF6{A=`7|hx|gFbSmi43PBqIf+`7;$4 z#y&WQ>M@lfYBJVPj$+g;)BR{JJRyu;*=fLBk-lZ7n|rSQ=!nvTM$W zn_!fPe1<;nf%!ISPD`9wT!k57ExWjqtS=(Pm|v@Tn`u>;$5HMWiZ8BPxro00QCx{N z57tQJ)vJmtqjlPYi!qJHhrY_9G!#)Cz;F?FGuU zW|;5a0In*<}EeB9tS&44OoZ)kERp^qIV1ZJCzR%1D z7A>j8>;XE(uB$6c`9hatmGZ;tnmT6{z9m))-Bw_@gUTzT6{V_unorl@%X*ZhQy+!( zSucA7$UY`0+jfzJGC0w>vwPZc=j@ukt{W(#M3xYgjq`=XF@x5dx>@}m}hJ&xUJ9k7C*~L$8#PO*lwKf*#;*$FX3SOoG)*e?#E>TW>1Yn*x7H|3I5Rn-y!fxLe6l3hXubJ2UiMQju%^n9MYBhu{SN8`{ynTzYl3B z?6&Z`HS7#EH+0!<|DqEF`j#OD#(!h*$rc`O#=o@|&g1FT7S7}8wHD6)cD=!=ta3c1 z=OHMtK0I!2@WFRjIFF~FTR6+vEAXwzhy5WB_t7bEzFaPPEt^LGp9eDAYx z&X=A8puqOze4n&%mh+B<^L+1pgHt`q`QFjEk4u5&vz!qIC;sKL;Ay?LHs;F@b+5_@6g8>odx%YhJZ*uCKR*oR5T@ zy%s;q*>B-2Cq4y<5vW`pI9UI|0`C-fwZU1>5NH(YEqs`Uol7me&ER)fc*x-QSvd1Q zZQ(yQ{I6Shy1@qy(I>W^hgmr5d4z>?zE=r+6hyLpZZJ66ZKuHhD)26WKP}`?8qW6( z3vUCB!fp!>DHQKrvv8KP-@@Ar|Dwb73E5#Z4%WZQ;I{s&1wYY3I4Cq(IOn@T$axNY zEdK?YpZ!_z?-Km)3S7>UKNa}Lg8y4f{&k>HNHh=Ha=kNtq=jFv*_{(CoZCg3!N~?v z&r>XZmOsJ5dHo)?@Fr3WVWx#YYVdO{oY&pg8JzU_1P9ysc8j0&xy!=2yboD8m-lfY zU#`ENvG`g38y3#;Kece?KXNFQTK6-ueoqv*^q)L~b3I3tZ~+uE6EK!50FT`v%_`ob8`v{4@SYtuW*4KZypXvVJD?Nf-F%0?!io zZh_Ae@+S&>k>Hndss%3PTq|(Nf2+VH|Dytz{<+!UR9@*pj=tB;!kg7yIy63VvDMI>CRskaMXIe~ZAs68ygtxb&+BeDG&{ z@EtyQl6hYal~?uysRG|4^hp=E9B1bU{3XF(FL3GSs}0We!hYUt;q2!>7jmSZ-z;$1 zKi};m=bwUK_O~wx{!F3&8-jnYz`qpy69xXA5C7m)Fd?FfUD{d?J2@Dq*g zFk0~M6LN|LF6I1D;Ie)F(+7Xd2Y<`pq`$P!dxBruC+eR$lzSKv~gc7aQM-VwOeXOF;1pZiD| zg#7}S{DY3wbTZfDIM@!U0+;#>7r4|XEO05mNZ_Q;qdGfhk-%j;z0lyqEB);ffy;5Z zNyt(1sUQe93S73wKL|Ne{=EX1@*fj&NIuWEpBK2)|4kuB%HJh$DSwZUL-Kk4KX@3G z;=uWEw!r1Q?ks~-@u%b9dOTb3%W=6v@MjDDiv<6d0>4u5lb)C1pzw1Gk80TYt-wzQ z#`^rx;L4T)|GVH{EbxZ}K11Le1upe}M#z`_%!`6w%K1Rx(r%v$Inr+X1wWOc()e5Q zakxOBOR3Kh2Di%_68zGhVGCz_77ASId7h9j^{nvWuk+!*(ue;VAO2qpT#h?`6mqCs zx06v2?zZqdH0(TJ;mrS-h4cHAHd{FNx360`%inI{_nCZCj@KttU!>=q22Zo_yA3|t z;Kcha4)*7X7C-AV#lmkfa&j%4^~tyJ+YEo9g=d<5lKB?S^R9&kCq2KT!#uxKV(||( zep_MTY`1C)=XzOb;kzET%HnI8A zGNXUk!rA`Q4Nm34&s$^p&b0WOjGQ@wU+(Mv#o}lAcM1N0kpGayUuooT6ZkA)hgXE0 zI3Z_;;Qw0ie<|?U0{>db`9|OaLbyQS_I{bsCn=<0g%{$K>*ZL3lilKlJ|`KR<*?nx zSUA5w^jv|<@-7xQ<-zS}sla~B5;z!c5X5_VF^P27K=Y>?C_rgmv+8W z;Bz61_4$XvNxro6Qv#RcLbt)$ZtOn^Cqe)M$r&K@Jjvk9pQ*U8M=kI}1b>#mNmi-h z&lR}jzsTUkJ5b13EBJ}V?e6Cme!G$XI}7J=>K_)){rtlg&f~>;3+MOsy<_1gkU1`nL1)6pfBD`fC_7S4XSz{0t{78{&o(7UABA1Vbd`;DIoT>8VUmi#8I zp7WUC{}33<-)`|o4gb-@^$F=C%2#370ow@IeB<02u2tMBoX)rx^aD3{F^* z$hS`LF9wa}tQY+B4sph}3j966f7A$EAh4cnw_^=X`6dfF8G^qUG?qWz2cIwSQo(4i1%X#&!0uKs39};q?O|zbDf+~fz}H%G+Lg5&=dXfa-cR|55C2nwzY{XpPmVi93$*Q@X5p;=NQ09djuCn` z2>#_l{^f#S>iH|dUn%%+^x^-L;HSLV&VRRX*7IH=N80%@fy?>HGeXW#q0fr~r+iud zYeJ4}M~hAc69U<@8VB3CT;Nj9a)T2l<*c^&-vph)Uj+ZL!u~J%@W+qR={Qf;=R|{( zp0d2@0_QX&VYHAV^~|#P-`CkWXIl6!gD((rQZY^n<|du}xi} ztywtBPct~>d4iCCip9_4&SVQ``LhLH2U%?Ac|v}dz?TYsn)@?kKU*MF6 z<#Y-;vK@VG@w1-sr;!zU?7(^svT&9kG`Ov2zQxaWn{DB2xBnJ$I=ENZwP++W)9;!eDF?zZ-ZRs-z#vk1?w4?NxAF(XFX!( zPZhZ24+(sO;K$pwRA9df=}Tvs!AZs#p?{sl&;HgR_)io3t1bT6b5IsO&*XcPkdq+^t+GgipiXz}y?td}gD<-cj+EdO00f1Hs2vBl5rbhqFiFZcr!bivvF z1;!3Z0;l%H`=mz*oa&~@@Q=1|&KED|Qi0{O{!q`Yh#qPY`0c1m3PUXXK7$`^ z;rv~>lPsL~F-BWB`w!lqrvmHqosmD;!f!G73=3y}o^9dWev2%8n6cXh7S8px%);6J zD-2HUVghvGesz__&-31!Eu8ha&BECa|7hXt=YO+s*5@9BlRol3_@fp-wP6bXvT&BO zRmhnr%JsU%KZ-UH5w;6l`c;>}NhkKJuPmJN{l?(LJ4xi5Fo|;M@lUo-l7+KoeBiq)(R6=L~_%`9{Q&GsW2XEDL8ppJU$%v%SRvxA^B7`L_w&6Y~Ega4G*zANluM{0oizCoG)HwaLQSpP#XCu9s~FCtZF5 z*=*;x1^>^5{vQZj>c3OSk@fqf#b0XlADRuJ2<&gH=dl*fdWHqQIae)0_q=X%+0 zaMEX*u=9}9N!uR3V*aBHPW-uof282Q1~j(A7{ULvz|Rr<(mo3Wza!+A30&@J>Rk9v{7LY;-~1C%C!{-*W+OZCw-)xqXoY!IP3YXg>!iW zKhXteJFxyoSUBsSYH(Zs;TAv3A7$aJ=Qs;zJ%3{1tj~N4XMGkL+}3A_#n1V!uyD?I zm4&nZms>dN^Gge7eSU3lTc2Aje%9wt7S8(IYvHWVLl(~ZY_)LK=Ou&N`n+lJv-}S& zob}vg;jHI(7S8%4P3`Shtj`dG+xi?UaM@2zw&aY`Vx0vR&i(UZA?Hld?|BygD9|We zXW=a81|jDxA?Fs0f1#1{M+;~9f3t9we~*w~Aml$PaQVE%dVx#(?6Bmsed2Re@nYj3 z=bLD7vcI&?Sc{+a&$4iqbB2&JOXwf5_<6lB%feay*%r?7&lB>ceM$u`?Xz6q(moei z^4lC8?zZqLrXC*)YdYCSj;En%8s~nJ`L|d&?|VFF;kWDL&JGLbeUF_sZuI%w!ZQuN z&%)Vm-y59tp95YVUlMXH{p0j?vV}ik^gLSNTY<5hlLbzBu>PYhob?%JaN?EwMP~?p zIo_Qqa7q(3D_zeQTR@m+L z2B$Jnn_zoBAoz*)zLB%e2Y=GSZ!!EYSva?^H!Ym&B-a=knHCIG1;o!4r`DTw(ve zS^Swsp9ci~OJJ=3Qv&}Ffxl+(1YkP_zFqJWtrQ1^odW+Au35g5r^u?`mhulbILVOm zrwDw3DAybz=XxQ3zTjUd`0o%neY=hIc|_o3W5!<=xa@ED3w)8_KWw@#7wNxP;71F* zSm5abUm|eV;N)*Nh;q#q`2SPd-3M7!)^Py8VI`2o2}vnPt|E!1>j5|22`6`ya((6a zQc=F`x*)4A?1|kRC>Y7G@crnhq4)xdm`2Xke4)t-wUmtVGBL#zlQc0(nRX{FNh*7O z_xb*=-}`OvUp+H-&v`!g_xzscyr28BySI|hCjS=sd6a)Mc^}2EBp;;sUK-CeqQ?qk7p_Q?KGYhl%L15j^cSd-=TOO&tnwN*Ow>Bc|2RlSJ8O3Q+^)L zTNKaZd7I*SJfpfo(ok$W^6R*SoX2wr`5mE+Z?EZa+wMG`E{eaC;?HUke=)`1Me#!| z;xDIo-o96o^K#uvemCV`(<1*5D4y5nMvCY4`8>tjC7~}otZX6Y^|_0Db%^uz`8r(J z=N^jZ{pVdLSub%ra=&BA*M#Ri|3~4Pe+I?#@^(@@FYhT7Z_DN1A2^r%9_n`<<>CG0 zN{Z)xucmnJ_ZEuh-eioCz12-?<}P}4^Y1=D4v(= zE{cDU;vZ-c|6_{(4#hv;BK{BLd|&i3<+1a?vhc9-Z}Rn_?(1#jsr9Gp_sBf{N#vY= z3f$Jet!uyUok9Lx%KsV4!}-r6=ls2t$MXB@oyFu2QU2wWhx4x@=lp9ZkFB3M;bG+o za(fTS*U!_Ghx2bG=ljp?l*jU4Yso|T8~MYb?)m>sc{u-=)9TIY>zMQJ54ZJi`B%iS z(t#Au_q~UapHKIPN0RgRtd67nd_F&!+_sAydsZ^>wUJBbh_8nCh}%7cm5bn(`|a@X z^>ZotOXN!^&k*_7DE<}l6%;>**27H{KbQQ66mMhl@AEt-?%%`NBJSV6`W-p%Z#&?Y z`}5TApDF(5H2(LV9x9>Ob{`WSHE_{Oc$Ki*PKN8Hh@6m7@%f&R#Pmy0j-bK!@<7QEQ%QZ2yv2q^8 z^ZwsU@wQ#MEjX0H7QR^Amv?y!zlNOmw_D&gM&936QM`@E^RE%_4$oOxM|m!#^|M~$ zJ z9t!vU4X4f0XIsQyK=HgjFB0#z&O(__ZhcR*jzYPN@-LuqE|K^$;=d{G=Yc0E4=>kK z5rr~-$%~pvGwFn zgjnCN9;f`9$+uJdjpTnMw`Io1)1_C%ef<9v_v2#pnW1qgwto11m@#n6&Ev6HD;95$ z-&-XVi?iZBI_xV}M#f3QUqgK#@wZS<#O<|>6+hp5e!DldGCock`IqB-JyHA_EPq-2 zIrzciKZj2he*y06)yMNIcq;L~f%|^v@xOz2Nc@X%->*I1w|A$+zl`{q;;+EF#QzFE zRs3~04zI@gd=u{bwU1{He759y8}9p;$M1#xsaxXrhR+ed0(ttxeZTA%-xqmu@%`aL z;uGMD#3#X*h);$u6F&sLT>Rtk72-$2uN6NEUWgw9zgc_+e5Lp=vH#y8egfiGi=U0_ z&|2|R5dUrQ8xX%3sQ5X^zd?Kow)>Ofy@=l^J`esA@yn2BllTDQH;X@t z_^SA+@U7xsMV@Wqm&3P)Ui2<14j zua4Dy{E-o+iXYV$0nHlmd&9dVe%s*@vKHPgzF|xRKOTKN{fH|h{yNkv;(t0I!VdAL z4vSC|{||h(cn4O%NL*)pJX;TmfeFQbFwFPaO;#vroYdEk`FwaLJ_OIj7r_hhrSOXQ z*Wgw0E8#WqtKjyxZmihE#(5pweizJ&`;G8S{1$jFejB_HzY|^&Ujwg--v_UWKLAf` zC#G?J7oLhg3irP^Z0T%yzYotP{)g~F{3&=vd=tDXz8PK<{}nvJ)W@?8^;G;t)HCs& zsORFZpk9dAP_Kx;fqGSZ59;2|$LTkE$-8aszq6mb2lZ5Z6zZ9{x6j2tgnA)95%o$7 zuZkatdQE%^>dAYW$A2h16+Z%=i605i#gBp);>W-%;xph?@#Ek%@y7A{-sbV2fcRA0 zkJC*2M8xOfemoZ9{{LAk;(lJLiqG=#i=PEg_HG{k7k&KVJ@8Dt51xw;zzgxq;1%(u z@T$0i&C!E54vo=-+KkKfPp zsrX~alZihG&&8jC7vj&uE8<(=Rq<``n)r+GWOVcR{koHi`*kN1e+hYV@mJx6_#5zw z_#Svwd<2f$n)oPq^1kNrC-79RnUte-@zrGaWetoHk`}L(No?yGw#Qpk`yuW$;etk(>cqV=T)@LqW_Vq9B z_lFg6zdx*s`~6`}-0u&Q4>XV8?^{xFzi;vXC+OF4zi-JU-tSuqaldb=h)>4yR>l2( ztS0XFV+ro}eLRODPb%*BMVYwY7vbY4__Od_d^5Zd-wLmYZ--aKcfxDpe}O0aHjn>r@KpRQ zcqTpq_f@(0Xm}w$7G4n_53h=s;WhE8@MK)`_)~Z)-T}|VKLyXlKLansPls2;KL@Xh zpAWBz&w(c&Y94<-JQW{;XW~oXx%hH;A$~QyB7PIRDtCVme**{^y055iM%zhBS9 zHy}P2-v}?nH^D37e&1ge|25)k;=hL{`!|pOC3q_S8axyC^LZ}r=hZ^o&uo&ogn~4|8$fPYQA0jumlV&sA~1uGPf-`jULOdHjB!PsROw zn~D4RFc?^SU>9&6%$yd)oK9>4F;W%0ML|4bDhf!B4Z_-J^C_*i(S z_($Mf;s?Pq@x$Q$f2{oaJ00FF@t=hEi60No#eMvX#7{>2GVwFvE5yGDFT^i|uN1!o zUOuRKyDWrH75D9uihl+19pYEOJIQ|#yQ=@}BDY(H>k*%k+o@>wh}fuGd=5ek8VoJ#Di-&Z*)F&f_V$?I->^ zrHkBVJw7A1_z5Ve9AA)I{BGnghj}aZG(zh2_WI9v952sHUE)Wf zey;dAsORGSs4o}44)vAdx1hdW{2|oe5cmCUA8bcou9s0iR(uu~+I}a?ipLMaeSdQw zgV+1lN_;nbyZBe|zETJBd7fkNdT&VFKcBi*{8_|r5I-P^*FEn3dEz38_s>IDiVq%v^&sw_tJcK*bJ&TvzInfX-fI{4^ZBXb{yFb5asOP{ z-w*fv_fC)F+$Hgk!~Kgs9{)4=v3UR4eI`y|>%}krWX!)&{1Nz0@ssd{r*<62o`2GD zG0$xA=ip1k2RdWCzhCWn7Q^3=_#Yh~V^JC5AE*@9$WDc1qFyuMaj{|I4ljPlU2sc8qde z-}!M>m>sVFsk37L{(8>(e}8yr|0{!8q;CK6l>tNX_*jd5*}U~><^J%n_G7I=Wh&ZF zB(qs>=hH`n4cER4?YpGCkH@D^wP;_UJ#I4_S-kGk7tlU!M8uiZXph^=#=l%i+4Yd89;KS`d%g%@+ z_4_p2e{5Iw_ExttT;JxiZ?vD`|2$2OQ@f4+X?SRRJ0}0<*bCPASojf2#|Prnf5uUH WEq+&Nxc0m4mu5oo?^$~bZU0}*R}KXL