├── .Xresources ├── .gitmodules ├── .xinitrc ├── README.md ├── cutexterm.png ├── cutexterm_tmux-sixel.png ├── debian-xterm.desktop ├── iosevka-ss04.tgz ├── tabbed-changes ├── config.def.h └── tabbed.c └── terminfo.tgz /.Xresources: -------------------------------------------------------------------------------- 1 | ! Allow regular colors, italics and underline 2 | XTerm*underLine: true 3 | XTerm*VT100.colorMode: True 4 | XTerm*VT100.colorBDMode: false 5 | XTerm*VT100.colorITMode: false 6 | XTerm*colorULMode: off 7 | XTerm*italicULMode: false 8 | 9 | ! Sixels support with a matching palette 10 | XTerm*decTerminalID: 340 11 | XTerm*numColorRegisters: 256 12 | 13 | ! Matching terminfo for all that 14 | XTerm*termName: xterm-new 15 | 16 | ! Dont toggle the 8th bit 17 | XTerm.vt100.metaSendsEscape: true 18 | 19 | ! Dont send Ctrl-H for Backspace 20 | XTerm.vt100.backarrowKey: false 21 | XTerm.ttyModes: erase ^? 22 | 23 | ! Allow escape codes that default to no on debian 24 | XTerm*allowFontOps: yes 25 | XTerm*allowTcapOps: yes 26 | XTerm*allowWindowOps: yes 27 | XTerm*dynamicColors: True 28 | 29 | ! No border 30 | XTerm*borderWidth: 0 31 | XTerm*VT100.internalBorder: 0 32 | 33 | ! Geometry adapted to my 4k screen 34 | XTerm.VT100.geometry: 88x23 35 | XTerm*fullscreen: true 36 | 37 | ! Cursor with no blink 38 | XTerm*cursorBlink: False 39 | 40 | ! Scrollbar 41 | XTerm*rightScrollBar: True 42 | XTerm*saveLines: 4096 43 | 44 | ! Dont sync 45 | XTerm*fastScroll: True 46 | ! Use other fast scroll hints 47 | XTerm*jumpScroll: True 48 | XTerm*multiScroll: True 49 | 50 | ! Scroll on key input, not on char output 51 | XTerm*scrollkey: True 52 | XTerm*scrollTtyOutput: false 53 | 54 | ! Locale settings 55 | !XTerm.vt100.locale: false 56 | XTerm.vt100.utf8: true 57 | XTerm.VT100.utf8Title: true 58 | !XTerm.vt100.wideChars: true 59 | XTerm*menuLocale: "C" 60 | 61 | ! No sound, visualbell only, and cause focus 62 | XTerm*visualBell: true 63 | XTerm*popOnBell: true 64 | XTerm*bellIsUrgent: true 65 | ! Minimal visualbell 66 | XTerm*visualBellLine: true 67 | XTerm*visualBellDelay: 20 68 | 69 | ! Selection 70 | XTerm*highlightSelection: true 71 | 72 | ! Select URL with a custom regex 73 | !XTerm*on2Clicks: regex [^ ''""()<>$+]* 74 | 75 | ! Extend selection to words 76 | !XTerm*trimSelection: true 77 | 78 | ! Dont disallow HT (TAB) in paste; i.e. do not convert to space character 79 | XTerm*VT100.DisallowedPasteControls: BS,DEL,ESC 80 | 81 | ! Double-click to select whole URLs 82 | XTerm*charClass: 33:48,36-47:48,58-59:48,61:48,63-64:48,95:48,126:48 83 | 84 | ! bad idea to select into the clipboard for https://shallowsky.com/blog/linux/x-selection-keys.html 85 | XTerm*selectToClipboard: false 86 | ! by default, use primary to avoid overwriting what everybody uses, iff ctrl-c send to clipboard 87 | XTerm*VT100*translations: #override \n\ 88 | :select-end(SELECT, CUT_BUFFER0, PRIMARY) \n\ 89 | Ctrl C: copy-selection(CLIPBOARD, PRIMARY) \n\ 90 | Ctrl V: insert-selection(CLIPBOARD) \n\ 91 | Shift Ctrl V: insert-selection(PRIMARY) \n\ 92 | Shift Insert: insert-selection(CUT_BUFFER0,PRIMARY,CLIPBOARD) \n\ 93 | Shift Ctrl Insert: insert-formatted("\033[202~%S~%s", CUT_BUFFER0,PRIMARY,CLIPBOARD) \n\ 94 | Ctrl +: larger-vt-font() \n\ 95 | Ctrl -: smaller-vt-font() \n\ 96 | Shift Ctrl B: set-scrollbar(toggle) \n\ 97 | Shift Ctrl S: set-scroll-on-key(toggle) \n\ 98 | Shift Ctrl R: hard-reset() \n\ 99 | Shift Ctrl M: maximize() \n\ 100 | Shift Ctrl R: restore() \n\ 101 | Ctrl Shift O: print(noAttrs, noNewLine) \n\ 102 | Ctrl Shift H: set-altscreen(toggle) \n\ 103 | Shift Home : scroll-to(begin) \n\ 104 | Shift End : scroll-to(end) \n\ 105 | Meta Prior: scroll-back(1,halfpage) \n\ 106 | Meta Next : scroll-forw(1,halfpage) \n\ 107 | Meta Home : scroll-to(begin) \n\ 108 | Meta End : scroll-to(end) \n\ 109 | 110 | ! WONTFIX: ctrl-pageup/down are used by Tabbed 111 | ! instead use alt-pageup/down to scroll, completed by matching home/end 112 | 113 | ! FIXME: upon start, would need to acquire the clipboard content 114 | ! Ctrl V: insert-selection(PRIMARY) \n\ 115 | ! Shift Ctrl V: insert-selection(CLIPBOARD) \n\ 116 | ! Shift Insert: insert-selection(CUT_BUFFER0,PRIMARY,CLIPBOARD) \n\ 117 | ! Shift Ctrl Insert: insert-formatted("\033[202~%S~%s", CUT_BUFFER0,PRIMARY,CLIPBOARD) \n\ 118 | 119 | ! FIXME: finding in the scroll buffer is not working yet 120 | ! Shift Ctrl F:dabbrev-expand() \n\ 121 | ! Meta /:dabbrev-expand() 122 | 123 | ! Extract url with ctrl-shift-o 124 | ! XTerm.vt100.printerCommand: xterm-open-url.sh 125 | 126 | ! Most people think selecting only into the clipboard is a bad idea 127 | ! cf https://shallowsky.com/blog/linux/x-selection-keys.html 128 | ! But if you disagree, use: 129 | !XTerm*selectToClipboard: true 130 | !*VT100*translations: #override \n\ 131 | ! :select-end(SELECT, CUT_BUFFER0, CLIPBOARD) \n\ 132 | ! Ctrl C: copy-selection(CLIPBOARD) \n\ 133 | ! Ctrl V: insert-selection(CLIPBOARD) \n\ 134 | ! Shift Ctrl V: insert-selection(CLIPBOARD) \n\ 135 | ! Shift Insert: insert-selection(CLIPBOARD) \n\ 136 | ! Ctrl +: larger-vt-font() \n\ 137 | ! Ctrl -: smaller-vt-font() \n\ 138 | ! Shift Ctrl M: maximize() \n\ 139 | ! Shift Ctrl R: restore() 140 | 141 | !Background & foreground 142 | !XTerm*background: #002B36 143 | !WONTFIX: a pure black is better on OLED 144 | XTerm*background: #000000 145 | XTerm*foreground: #D2D2D2 146 | XTerm*borderColor: #343434 147 | 148 | !Colors: Solarized dark 149 | !------------------------- 150 | !black 151 | XTerm*color0: #222222 152 | XTerm*color8: #454545 153 | 154 | !red 155 | XTerm*color1: #9E5641 156 | XTerm*color9: #CC896D 157 | 158 | !green 159 | XTerm*color2: #6C7E55 160 | XTerm*color10: #C4DF90 161 | 162 | !yellow 163 | XTerm*color3: #CAAF2B 164 | XTerm*color11: #FFE080 165 | 166 | !blue 167 | XTerm*color4: #7FB8D8 168 | XTerm*color12: #B8DDEA 169 | 170 | !magenta 171 | XTerm*color5: #956D9D 172 | XTerm*color13: #C18FCB 173 | 174 | !cyan 175 | XTerm*color6: #4c8ea1 176 | XTerm*color14: #6bc1d0 177 | 178 | !white 179 | XTerm*color7: #808080 180 | XTerm*color15: #cdcdcd 181 | 182 | ! For regular sized screens (not 4k) 183 | ! apt install fonts-terminus xfonts-terminus xfonts-terminus-oblique fonts-inconsolata 184 | ! Default font, used for drawing the menu 185 | !XTerm*font: 12x24 186 | ! Ctrl-click menu choices 187 | !XTerm*VT100.font1: terminus-12 188 | !XTerm*VT100.font2: terminus-12 189 | !XTerm*VT100.font3: terminus-16 190 | !XTerm*VT100.font4: terminus-18 191 | !XTerm*VT100.font5: terminus-20 192 | !XTerm*VT100.font6: terminus-22 193 | !XTerm*fontMenu.Label: VT Fonts 194 | !XTerm*fontMenu*fontdefault*Label: Default 195 | !XTerm*fontMenu*font1*Label: Unreadable 196 | !XTerm*fontMenu*font2*Label: 12-points 197 | !XTerm*fontMenu*font3*Label: 16-points 198 | !XTerm*fontMenu*font4*Label: 18-points 199 | !XTerm*fontMenu*font5*Label: 20-points 200 | !XTerm*fontMenu*font6*Label: 22-points 201 | 202 | ! Aliasing and hinting is question of tastes 203 | ! I like it so I keep it even in 4k 204 | ! Xft.autohint: 0 205 | ! Xft.antialias: false 206 | ! Xft.hinting: false 207 | 208 | ! For 4k screens 209 | ! Default font, used for drawing the menu 210 | XTerm*font: -*-terminus-*-r-*-32-*-*-*-*-*-*-* 211 | ! Ctrl-click menu choices 212 | XTerm*font1: -*-terminus-*-r-*-18-*-*-*-*-*-*-* 213 | XTerm*font2: -*-terminus-*-r-*-20-*-*-*-*-*-*-* 214 | XTerm*font3: -*-terminus-*-r-*-22-*-*-*-*-*-*-* 215 | XTerm*font4: -*-terminus-*-r-*-24-*-*-*-*-*-*-* 216 | XTerm*font5: -*-terminus-*-r-*-28-*-*-*-*-*-*-* 217 | XTerm*font6: -*-terminus-*-r-*-32-*-*-*-*-*-*-* 218 | XTerm*fontMenu.Label: VT Fonts 219 | XTerm*fontMenu*fontdefault*Label: Default 220 | XTerm*fontMenu*font1*Label: Terminus 18 221 | XTerm*fontMenu*font2*Label: Terminus 20 222 | XTerm*fontMenu*font3*Label: Terminus 22 223 | XTerm*fontMenu*font4*Label: Terminus 24 224 | XTerm*fontMenu*font5*Label: Terminus 28 225 | XTerm*fontMenu*font6*Label: Terminus 32 226 | XTerm*faceSize: 22 227 | ! For a 4k display, matching hight for the menu: 228 | XTerm*menuHeight: 2000 229 | 230 | ! No ligatures support yet, so iosevka is not shining 231 | XTerm*faceName: "Iosevka SS04 Light" 232 | XTerm*boldFont: "Iosevka SS04 SemiBold" 233 | ! Patched iosevka for nnn icons 234 | !XTerm*faceName: "Iosevka Nerd Font Mono" 235 | ! Pached ubuntu font for powerline 236 | !XTerm*faceName: "Ubuntu Mono for Powerline" 237 | !XTerm*boldFont: "Ubuntu Mono Bold" 238 | 239 | ! FIXME: A pointer of the same approx size is not working 240 | !Xcursor*size: 32 241 | Xcursor.size: 64 242 | ! FIXME: The following can help making it more visible 243 | !XTerm*pointerColor: blue 244 | !XTerm*pointerColorBackground: red 245 | 246 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tabbed"] 2 | path = tabbed 3 | url = https://git.suckless.org/tabbed 4 | -------------------------------------------------------------------------------- /.xinitrc: -------------------------------------------------------------------------------- 1 | # disable bell 2 | xset -b 3 | 4 | # if you want to change your keyboard settings 5 | [ -f ~/.Xmodmap ] && xmodmap ~/.Xmodmap 6 | 7 | # load xterm defaults 8 | [ -f ~/.Xresources ] && xrdb -merge ~/.Xresources 9 | 10 | # in case of problems with the defaults or the .desktop XDG starter file 11 | tabbed -n XTerm xterm -fn -freetype-inconsolata-medium-r-normal--64-480-96-96-c-240-iso10646-1 -fa "Iosevka SS04 Regular" -fs 20 -fb "Iosevka SS04 Medium" -ti vt340 -u8 +bc -bdc -itc +nul +ulc -ulit -geometry 88x23 -si -sk -xrm "XTerm*numColorRegisters: 256" -j -s +cm +dc -rightbar -sl 4096 +vb +pob +vb -tn xterm-new -b 0 -bw 0 -into || xterm -fn -freetype-inconsolata-medium-r-normal--64-480-96-96-c-240-iso10646-1 -fa "Iosevka SS04 Regular" -fs 20 -fb "Iosevka SS04 Medium" -ti vt340 -u8 +bc -bdc -itc +nul +ulc -ulit -geometry 88x23 -si -sk -xrm "XTerm*numColorRegisters: 256" -j -s +cm +dc -rightbar -sl 4096 +vb +pob +vb -tn xterm-new -b 0 -bw 0 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CuteXterm](cutexterm.png) 2 | 3 | ## What is CuteXterm? 4 | 5 | CuteXterm is a set of sensible defaults to make the terminal experience on 6 | Linux as good as possible, by adapting xterm to the 21st century. 7 | 8 | To be precise, CuteXterm is a set of software (tabbed), fonts (iosevka), 9 | and configuration defaults (Xresources, xinitrc, terminfo, application desktop 10 | file, shell commands) that together make xterm cute and functional. 11 | 12 | This way, you can get something like that when you run tmux-sixel inside it: 13 | ![cutexterm_tmux-sixel.png](cutexterm_tmux-sixel.png) 14 | 15 | ## What is the difference over say gnome-terminal or even just xterm? 16 | 17 | With CuteXterm settings, you will get xterm just as before, but: 18 | - with sixel graphics, 19 | - using a modern free software TrueType font that look good even on hi-res displays, 20 | - with a legible menu that lets you change the font, 21 | - with Unicode / Dim / Bold / Italics / Underline ANSI text support, 22 | - with a terminfo and termcap definition advertising that xterm supports all 23 | the above, 24 | - with intuitive shortcuts similar to your browser, 25 | - with various other "sane defaults" which includes things I like, such as 26 | Solarized colors and consistent shortcuts, and that I believe you will like 27 | as well 28 | 29 | If you don't like my settings, or if your screen is not 4k, you may want to try 30 | the alternative settings provided inside the .Xresources - but to be honest, 31 | they shouldn't matter so much: to me, the core terminal experience is that the 32 | font looks good, the keyboard interactions feel good, and the user interface 33 | doesn't waste too much space. 34 | 35 | Also, I've become fond of tabs! 36 | 37 | ## Wait, xterm supports tabs? 38 | 39 | Yes, xterm can be made to support tabs, if you use `tabbed` and a few tweaks to 40 | the file that starts xterm. 41 | 42 | tabbed has been changed in config.def.h to use Ctrl for shortcuts and PageUp 43 | PageDown for tabs, adding an extra Shift for key sequences you rarely do like: 44 | - Ctrl-Shift-T to open a new tab, 45 | - Ctrl-Shift-Q to close a tab, 46 | - Ctrl-Shift-PageUp or PageDown to reorder a tab 47 | 48 | Ctrl without shift is used with the frequent actions, like: 49 | - Ctrl-Tab to change tabs, 50 | - Ctrl-PageUp to go to the next tab, 51 | - Ctrl-PageDown to go to the previous tab, 52 | - Ctrl-number to go to the Nth tab 53 | 54 | F11 can toggle fullscreen, which is on by default. 55 | 56 | ## What about xterm shortcuts? 57 | 58 | Again, rare actions are done with Ctrl-Shift, like: 59 | - Ctrl-Shift-B to toggle the scrollbar, 60 | - Ctrl-Shift-S to lock the scroll while typing, 61 | - Ctrl-Shift-R to reset the screen (follow that by Ctrl-L if needed to initiate a redraw), 62 | - Ctrl-Shift-M to maximize, Ctrl-Shift-R to restore (FIXME: this would be better as a toggle), 63 | - Ctrl-Shift-O to run a script (which I use to open URLs), 64 | - Ctrl-Shift-H to switch to the alternative screen (like from vim) 65 | 66 | For the most frequent actions, Ctrl is used like: 67 | - Ctrl-Plus to increase the fontsize, 68 | - Ctrl-Minus to decrease the fontsize, 69 | - Ctrl-V to paste (with Shift-Ctrl-V and Shift-Insert as slightly different alternatives), 70 | - Ctrl-C to copy 71 | 72 | The copy-paste actions could be improved, but this would require changes to 73 | xterm (like to copy/swap the paste buffers when switching to xterm) which may 74 | not be acceptable to the maintainer. 75 | 76 | ## Wait, why does Ctrl-C copy? 77 | 78 | Because we are in the 21st century! 79 | 80 | Using Shift+Ctrl+C to copy is horrible, and a pure torture when switching back 81 | and forth between everything that can copy without pressing the shift key (such 82 | as the browser) and the terminal. 83 | 84 | By the way, there is no reason why Ctrl-C couldn't do both copy and send 85 | SIGINTR - it should just be made context-aware, so that it does a copy only if 86 | something is selected (like with the mouse), and send a signal only if nothing 87 | is highlighted. 88 | 89 | Yes, I know about select-to-copy - but when I highlight text, I 90 | don't necessarily mean to copy it. And I don't see why I have to adapt my 91 | muscle memory to the terminal, instead of the other way around! 92 | 93 | This is possible with some minor configuration tweaks that do not require any 94 | serious change (like what would be required to bring context awareness): the X 95 | key next to C could be used to send SIGINTR, so Ctrl-C always means copy and 96 | Ctrl-X always means SIGINTR. 97 | 98 | While we're at it, let's remove Ctrl-Q and Ctrl-S that often accidentally cause 99 | a scroll lock, by disabling flow control with: 100 | 101 | stty intr ^X stop undef start undef rprnt undef discard undef swtch undef -ixoff -ixon 102 | 103 | With the above, stty -a doesn't show ^S,^Q,^R and ^O anymore, so you can remap them to your own uses: 104 | 105 | ```{stty -a} 106 | speed 38400 baud; rows 23; columns 98; line = 0; 107 | intr = ^X; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; 108 | swtch = ; start = ; stop = ; susp = ^Z; rprnt = ; werase = ^W; 109 | lnext = ^V; discard = ; min = 1; time = 0; 110 | -parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts 111 | -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -ixon -ixoff -iuclc -ixany 112 | -imaxbel iutf8 113 | opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 114 | isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho 115 | -extproc 116 | ``` 117 | 118 | ## How to install CuteXterm 119 | 120 | Until someone perpare some packages, the installation is manual: 121 | 1. Untar iosevka-ss04.tgz in ~/.fonts or /usr/share/font (system-wide deployment) 122 | 2. Untar terminfo in.tgz in ~/.terminfo or /usr/share/terminfo (system-wide deployment) 123 | 3. Put .xinitrc and .Xresources in ~/ or /etc/skel (system-wide deployment) 124 | 4. Put the XDG starter file debian-xterm.desktop in ~/.local/share/applications/ or /usr/share/applications/ (system-wide deployment) 125 | 5. Put tabbed/tabbed in /usr/local/bin ideally after recompiling it 126 | 6. Install xpropr and xdotool then put at the bottom of your .zshrc or .bashrc: 127 | 128 | ```{bashrc} 129 | # No Ctrl-C, instead use Ctrl-X to stop 130 | stty intr ^X stop undef start undef rprnt undef discard undef swtch undef -ixoff -ixon 131 | 132 | # Remove the terminal titlebar 133 | [ ${TERM} = "xterm-new" ] && \ 134 | xprop -id `xdotool getactivewindow` -format _MOTIF_WM_HINTS 32i -set _MOTIF_WM_HINTS 2 135 | ``` 136 | 137 | You are then ready and there's no need to reboot: to load these defaults, simply type: 138 | 139 | ```{xrdb .Xresources} 140 | xrdb .Xresources 141 | ``` 142 | 143 | You can then start xterm from your menu. 144 | 145 | ## Why did you make CuteXterm? (WARNING: LONG RANT) 146 | 147 | I'm more of a Windows fan, but recently I've decided to try using Linux on a 148 | laptop- and OMG I was *shocked*! While most people praise Linux, personally I 149 | couldn't believe just how bad the default experience was, in comparison to what 150 | I was using only days before!! 151 | 152 | First, I thought it was just a culture shock, then a bad choice of terminal, or a 153 | bad default configuration maybe? 154 | 155 | But no, as more weeks went by, and I noticed every terminal emulator I tried 156 | suck, in various degrees, and for a different reason each time, I realized that 157 | it was just how things are on Linux - half assed. 158 | 159 | However, as I spend a lot of time in terminals, I had to make do and figure 160 | something that would work. 161 | 162 | My first idea was a GPU passthrough to run Windows 10 with low latency. But 163 | that would defeat most of the experience of trying to use Linux. As I was in 164 | the bargaining stage, I thought it could be justified if crucial apps were 165 | missing - but since my favorite browser (Microsoft Edge) is now available on 166 | Linux, meddling with qemu-kvm just to get a good terminal like mintty seemed a 167 | bit overkill. 168 | 169 | So I took the time to study the situation, and prepared a acceptable 170 | replacement for my own use, by fixing what could be fixed, at least enough 171 | that I doesn't make me want to start Windows whenever I need to ssh somewhere. 172 | 173 | There are still many warts (like no programming ligatures, changing fonts takes 174 | far too long, there are some weird sixel redraw issues and they are not scaled 175 | when changing fonts) and the copy-paste between the browser and the terminal 176 | could (and WILL!) be improved - but at least I have now have a usable Linux 177 | terminal usable, close to being good enough for my needs 178 | 179 | ## Wait, I thought people said "Windows sucks"? 180 | 181 | LOL sweet summer child! 182 | 183 | In my experience, most people who say "Window sucks" are just showoffs who want 184 | geek creds. Millenials seem to have grown up using Linux, so they became 185 | accustomized to the bad terminal experience. And now, in a kind of Stockholm 186 | syndrome, they try to rationalize and justify the bad situation. 187 | 188 | But no, even without talking about things that are known to be problematic on 189 | Linux (such as dual screen with different resolutions - let's give Linux a 190 | pass) even in the very basic situation of a laptop with one screen, I'm sorry 191 | but the terminal experience is just *far* better on Windows 10 with mintty than 192 | on Linux: it doesn't just feels snappier and looks better (even if both are 193 | very important), it's just better in every single way, in term of font support, 194 | configurability, etc. 195 | 196 | And it's not just mintty that rocks: Microsoft Terminal is also extremely 197 | promising. Once some issues are fixed, and important features added (like 198 | [Sixel Support](https://github.com/microsoft/Terminal/issues/448) which is a 199 | must for me) it may become a worthy contender to mintty thanks to its native 200 | tab support. 201 | 202 | ## I disagree for reason X, Y and Z 203 | 204 | Good for you! 205 | 206 | ## I agree, what did you find to be the least worst options on Linux? 207 | 208 | You may think about kitty, alacritty, etc. but mlterm and xterm are the 209 | only ones worth using if you can't compromise either on essential features 210 | (for me, sixel support) or performance ([the most important is 211 | latency](https://danluu.com/term-latency/) which [has been studied in 212 | depth](https://lwn.net/Articles/751763/)) 213 | 214 | Eventually, I settled on xterm, simply because I found it more versatile that 215 | mlterm, with a good enough latency. And when properly configured, xterm is not 216 | too painful to use: it can get out of the way. By that, I mean I can forget 217 | what I'm using and the limitations of my tools, and concentrate instead on what 218 | I'm doing. 219 | 220 | Every single other terminal I've tried just isn't as good: the rare ones that 221 | are not a pure waste of time and can support the comparison with xterm suffer 222 | from a high latency, or a larger memory footprint, or both (!!), while only 223 | offering a mere fraction of xterm features. 224 | 225 | Don't expect any miracle: even when it's well configured, xterm still pales 226 | in comparison of mintty - in a day-to-day use, what I miss the most is the 227 | support for programming ligatures, but if you look deeper, it's death by a 228 | thousand papercuts - like the slow redraw of sixels that sometimes happen. 229 | 230 | Yet in 2021, xterm seems to be the best overall choice if you spend a lot of 231 | time in the console on Linux. So CuteXterm tries to do the best with it. 232 | -------------------------------------------------------------------------------- /cutexterm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/CuteXterm/5270ff79e350514ea7f8ee18230cfca1534d57ce/cutexterm.png -------------------------------------------------------------------------------- /cutexterm_tmux-sixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/CuteXterm/5270ff79e350514ea7f8ee18230cfca1534d57ce/cutexterm_tmux-sixel.png -------------------------------------------------------------------------------- /debian-xterm.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Terminal 3 | GenericName=Terminal 4 | Comment=xterm with all the right options 5 | #Exec=tabbed -c -g 3188x2100 xterm -fa 'Iosevka SS04 Light' -into 6 | Exec=tabbed -n xterm -c xterm -fa 'Iosevka SS04 Light' -into 7 | Terminal=false 8 | Type=Application 9 | #Encoding=UTF-8 10 | Icon=org.gnome.Terminal 11 | Categories=System;TerminalEmulator; 12 | Keywords=shell;prompt;command;commandline;cmd; 13 | X-Desktop-File-Install-Version=0.24 14 | Comment=Use the command line 15 | #Actions=new-window;preferences; 16 | # 17 | #[Desktop Action new-window] 18 | #Name=New Window 19 | #Exec=gnome-terminal --window 20 | # 21 | #[Desktop Action preferences] 22 | #Name=Preferences 23 | #Exec=gnome-terminal --preferences 24 | -------------------------------------------------------------------------------- /iosevka-ss04.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/CuteXterm/5270ff79e350514ea7f8ee18230cfca1534d57ce/iosevka-ss04.tgz -------------------------------------------------------------------------------- /tabbed-changes/config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance */ 4 | static const char font[] = "monospace:size=9"; 5 | static const char* normbgcolor = "#222222"; 6 | static const char* normfgcolor = "#cccccc"; 7 | static const char* selbgcolor = "#555555"; 8 | static const char* selfgcolor = "#ffffff"; 9 | static const char* urgbgcolor = "#111111"; 10 | static const char* urgfgcolor = "#cc0000"; 11 | static const char before[] = "<"; 12 | static const char after[] = ">"; 13 | static const char titletrim[] = "..."; 14 | static const int tabwidth = 200; 15 | static const Bool foreground = True; 16 | static Bool urgentswitch = False; 17 | 18 | /* 19 | * Where to place a new tab when it is opened. When npisrelative is True, 20 | * then the current position is changed + newposition. If npisrelative 21 | * is False, then newposition is an absolute position. 22 | */ 23 | static int newposition = 0; 24 | static Bool npisrelative = False; 25 | 26 | #define SETPROP(p) { \ 27 | .v = (char *[]){ "/bin/sh", "-c", \ 28 | "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ 29 | "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ 30 | "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ 31 | "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ 32 | p, winid, NULL \ 33 | } \ 34 | } 35 | 36 | #define MODKEY ControlMask 37 | static Key keys[] = { 38 | /* modifier key function argument */ 39 | { MODKEY|ShiftMask, XK_t, focusonce, { 0 } }, 40 | { MODKEY|ShiftMask, XK_t, spawn, { 0 } }, 41 | 42 | // csdvrx : apt install libx11-dev libxft-dev to apply: 43 | // Use PageUp/Down instead of vi keys 44 | { MODKEY, XK_Page_Down,rotate, { .i = +1 } }, 45 | { MODKEY, XK_Page_Up, rotate, { .i = -1 } }, 46 | { MODKEY|ShiftMask, XK_Page_Down,movetab, { .i = +1 } }, 47 | { MODKEY|ShiftMask, XK_Page_Up, movetab, { .i = -1 } }, 48 | { MODKEY, XK_Tab, rotate, { .i = 0 } }, 49 | 50 | { MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, 51 | { MODKEY, XK_1, move, { .i = 0 } }, 52 | { MODKEY, XK_2, move, { .i = 1 } }, 53 | { MODKEY, XK_3, move, { .i = 2 } }, 54 | { MODKEY, XK_4, move, { .i = 3 } }, 55 | { MODKEY, XK_5, move, { .i = 4 } }, 56 | { MODKEY, XK_6, move, { .i = 5 } }, 57 | { MODKEY, XK_7, move, { .i = 6 } }, 58 | { MODKEY, XK_8, move, { .i = 7 } }, 59 | { MODKEY, XK_9, move, { .i = 8 } }, 60 | { MODKEY, XK_0, move, { .i = 9 } }, 61 | 62 | // Shift Ctrl Q to avoid accidental kills 63 | { MODKEY|ShiftMask, XK_q, killclient, { 0 } }, 64 | 65 | // Likewise, so disable the toggle 66 | { MODKEY|ShiftMask, XK_u, focusurgent, { 0 } }, 67 | // { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, 68 | 69 | { 0, XK_F11, fullscreen, { 0 } }, 70 | }; 71 | -------------------------------------------------------------------------------- /tabbed-changes/tabbed.c: -------------------------------------------------------------------------------- 1 | /* 2 | * See LICENSE file for copyright and license details. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "arg.h" 21 | 22 | /* XEMBED messages */ 23 | #define XEMBED_EMBEDDED_NOTIFY 0 24 | #define XEMBED_WINDOW_ACTIVATE 1 25 | #define XEMBED_WINDOW_DEACTIVATE 2 26 | #define XEMBED_REQUEST_FOCUS 3 27 | #define XEMBED_FOCUS_IN 4 28 | #define XEMBED_FOCUS_OUT 5 29 | #define XEMBED_FOCUS_NEXT 6 30 | #define XEMBED_FOCUS_PREV 7 31 | /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ 32 | #define XEMBED_MODALITY_ON 10 33 | #define XEMBED_MODALITY_OFF 11 34 | #define XEMBED_REGISTER_ACCELERATOR 12 35 | #define XEMBED_UNREGISTER_ACCELERATOR 13 36 | #define XEMBED_ACTIVATE_ACCELERATOR 14 37 | 38 | /* Details for XEMBED_FOCUS_IN: */ 39 | #define XEMBED_FOCUS_CURRENT 0 40 | #define XEMBED_FOCUS_FIRST 1 41 | #define XEMBED_FOCUS_LAST 2 42 | 43 | /* Macros */ 44 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 45 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 46 | #define LENGTH(x) (sizeof((x)) / sizeof(*(x))) 47 | #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) 48 | #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) 49 | 50 | enum { ColFG, ColBG, ColLast }; /* color */ 51 | enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, 52 | XEmbed, WMSelectTab, WMLast }; /* default atoms */ 53 | 54 | typedef union { 55 | int i; 56 | const void *v; 57 | } Arg; 58 | 59 | typedef struct { 60 | unsigned int mod; 61 | KeySym keysym; 62 | void (*func)(const Arg *); 63 | const Arg arg; 64 | } Key; 65 | 66 | typedef struct { 67 | int x, y, w, h; 68 | XftColor norm[ColLast]; 69 | XftColor sel[ColLast]; 70 | XftColor urg[ColLast]; 71 | Drawable drawable; 72 | GC gc; 73 | struct { 74 | int ascent; 75 | int descent; 76 | int height; 77 | XftFont *xfont; 78 | } font; 79 | } DC; /* draw context */ 80 | 81 | typedef struct { 82 | char name[256]; 83 | Window win; 84 | int tabx; 85 | Bool urgent; 86 | Bool closed; 87 | } Client; 88 | 89 | /* function declarations */ 90 | static void buttonpress(const XEvent *e); 91 | static void cleanup(void); 92 | static void clientmessage(const XEvent *e); 93 | static void configurenotify(const XEvent *e); 94 | static void configurerequest(const XEvent *e); 95 | static void createnotify(const XEvent *e); 96 | static void destroynotify(const XEvent *e); 97 | static void die(const char *errstr, ...); 98 | static void drawbar(void); 99 | static void drawtext(const char *text, XftColor col[ColLast]); 100 | static void *ecalloc(size_t n, size_t size); 101 | static void *erealloc(void *o, size_t size); 102 | static void expose(const XEvent *e); 103 | static void focus(int c); 104 | static void focusin(const XEvent *e); 105 | static void focusonce(const Arg *arg); 106 | static void focusurgent(const Arg *arg); 107 | static void fullscreen(const Arg *arg); 108 | static char *getatom(int a); 109 | static int getclient(Window w); 110 | static XftColor getcolor(const char *colstr); 111 | static int getfirsttab(void); 112 | static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); 113 | static void initfont(const char *fontstr); 114 | static Bool isprotodel(int c); 115 | static void keypress(const XEvent *e); 116 | static void killclient(const Arg *arg); 117 | static void manage(Window win); 118 | static void maprequest(const XEvent *e); 119 | static void move(const Arg *arg); 120 | static void movetab(const Arg *arg); 121 | static void propertynotify(const XEvent *e); 122 | static void resize(int c, int w, int h); 123 | static void rotate(const Arg *arg); 124 | static void run(void); 125 | static void sendxembed(int c, long msg, long detail, long d1, long d2); 126 | static void setcmd(int argc, char *argv[], int); 127 | static void setup(void); 128 | static void sigchld(int unused); 129 | static void spawn(const Arg *arg); 130 | static int textnw(const char *text, unsigned int len); 131 | static void toggle(const Arg *arg); 132 | static void unmanage(int c); 133 | static void unmapnotify(const XEvent *e); 134 | static void updatenumlockmask(void); 135 | static void updatetitle(int c); 136 | static int xerror(Display *dpy, XErrorEvent *ee); 137 | static void xsettitle(Window w, const char *str); 138 | 139 | /* variables */ 140 | static int screen; 141 | static void (*handler[LASTEvent]) (const XEvent *) = { 142 | [ButtonPress] = buttonpress, 143 | [ClientMessage] = clientmessage, 144 | [ConfigureNotify] = configurenotify, 145 | [ConfigureRequest] = configurerequest, 146 | [CreateNotify] = createnotify, 147 | [UnmapNotify] = unmapnotify, 148 | [DestroyNotify] = destroynotify, 149 | [Expose] = expose, 150 | [FocusIn] = focusin, 151 | [KeyPress] = keypress, 152 | [MapRequest] = maprequest, 153 | [PropertyNotify] = propertynotify, 154 | }; 155 | static int bh, obh, wx, wy, ww, wh; 156 | static unsigned int numlockmask; 157 | static Bool running = True, nextfocus, doinitspawn = True, 158 | fillagain = False, closelastclient = False, 159 | killclientsfirst = False; 160 | static Display *dpy; 161 | static DC dc; 162 | static Atom wmatom[WMLast]; 163 | static Window root, win; 164 | static Client **clients; 165 | static int nclients, sel = -1, lastsel = -1; 166 | static int (*xerrorxlib)(Display *, XErrorEvent *); 167 | static int cmd_append_pos; 168 | static char winid[64]; 169 | static char **cmd; 170 | static char *wmname = "tabbed"; 171 | static const char *geometry; 172 | 173 | char *argv0; 174 | 175 | /* configuration, allows nested code to access above variables */ 176 | #include "config.h" 177 | 178 | void 179 | buttonpress(const XEvent *e) 180 | { 181 | const XButtonPressedEvent *ev = &e->xbutton; 182 | int i, fc; 183 | Arg arg; 184 | 185 | if (ev->y < 0 || ev->y > bh) 186 | return; 187 | 188 | if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) 189 | return; 190 | 191 | for (i = fc; i < nclients; i++) { 192 | if (clients[i]->tabx > ev->x) { 193 | switch (ev->button) { 194 | case Button1: 195 | focus(i); 196 | break; 197 | case Button2: 198 | focus(i); 199 | killclient(NULL); 200 | break; 201 | case Button4: /* FALLTHROUGH */ 202 | case Button5: 203 | arg.i = ev->button == Button4 ? -1 : 1; 204 | rotate(&arg); 205 | break; 206 | } 207 | break; 208 | } 209 | } 210 | } 211 | 212 | void 213 | cleanup(void) 214 | { 215 | int i; 216 | 217 | for (i = 0; i < nclients; i++) { 218 | focus(i); 219 | killclient(NULL); 220 | XReparentWindow(dpy, clients[i]->win, root, 0, 0); 221 | unmanage(i); 222 | } 223 | free(clients); 224 | clients = NULL; 225 | 226 | XFreePixmap(dpy, dc.drawable); 227 | XFreeGC(dpy, dc.gc); 228 | XDestroyWindow(dpy, win); 229 | XSync(dpy, False); 230 | free(cmd); 231 | } 232 | 233 | void 234 | clientmessage(const XEvent *e) 235 | { 236 | const XClientMessageEvent *ev = &e->xclient; 237 | 238 | if (ev->message_type == wmatom[WMProtocols] && 239 | ev->data.l[0] == wmatom[WMDelete]) { 240 | if (nclients > 1 && killclientsfirst) { 241 | killclient(0); 242 | return; 243 | } 244 | running = False; 245 | } 246 | } 247 | 248 | void 249 | configurenotify(const XEvent *e) 250 | { 251 | const XConfigureEvent *ev = &e->xconfigure; 252 | 253 | if (ev->window == win && (ev->width != ww || ev->height != wh)) { 254 | ww = ev->width; 255 | wh = ev->height; 256 | XFreePixmap(dpy, dc.drawable); 257 | dc.drawable = XCreatePixmap(dpy, root, ww, wh, 258 | DefaultDepth(dpy, screen)); 259 | 260 | if (!obh && (wh <= bh)) { 261 | obh = bh; 262 | bh = 0; 263 | } else if (!bh && (wh > obh)) { 264 | bh = obh; 265 | obh = 0; 266 | } 267 | 268 | if (sel > -1) 269 | resize(sel, ww, wh - bh); 270 | XSync(dpy, False); 271 | } 272 | } 273 | 274 | void 275 | configurerequest(const XEvent *e) 276 | { 277 | const XConfigureRequestEvent *ev = &e->xconfigurerequest; 278 | XWindowChanges wc; 279 | int c; 280 | 281 | if ((c = getclient(ev->window)) > -1) { 282 | wc.x = 0; 283 | wc.y = bh; 284 | wc.width = ww; 285 | wc.height = wh - bh; 286 | wc.border_width = 0; 287 | wc.sibling = ev->above; 288 | wc.stack_mode = ev->detail; 289 | XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &wc); 290 | } 291 | } 292 | 293 | void 294 | createnotify(const XEvent *e) 295 | { 296 | const XCreateWindowEvent *ev = &e->xcreatewindow; 297 | 298 | if (ev->window != win && getclient(ev->window) < 0) 299 | manage(ev->window); 300 | } 301 | 302 | void 303 | destroynotify(const XEvent *e) 304 | { 305 | const XDestroyWindowEvent *ev = &e->xdestroywindow; 306 | int c; 307 | 308 | if ((c = getclient(ev->window)) > -1) 309 | unmanage(c); 310 | } 311 | 312 | void 313 | die(const char *errstr, ...) 314 | { 315 | va_list ap; 316 | 317 | va_start(ap, errstr); 318 | vfprintf(stderr, errstr, ap); 319 | va_end(ap); 320 | exit(EXIT_FAILURE); 321 | } 322 | 323 | void 324 | drawbar(void) 325 | { 326 | XftColor *col; 327 | int c, cc, fc, width; 328 | char *name = NULL; 329 | 330 | if (nclients == 0) { 331 | dc.x = 0; 332 | dc.w = ww; 333 | XFetchName(dpy, win, &name); 334 | drawtext(name ? name : "", dc.norm); 335 | XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 336 | XSync(dpy, False); 337 | 338 | return; 339 | } 340 | 341 | width = ww; 342 | cc = ww / tabwidth; 343 | if (nclients > cc) 344 | cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 345 | 346 | if ((fc = getfirsttab()) + cc < nclients) { 347 | dc.w = TEXTW(after); 348 | dc.x = width - dc.w; 349 | drawtext(after, dc.sel); 350 | width -= dc.w; 351 | } 352 | dc.x = 0; 353 | 354 | if (fc > 0) { 355 | dc.w = TEXTW(before); 356 | drawtext(before, dc.sel); 357 | dc.x += dc.w; 358 | width -= dc.w; 359 | } 360 | 361 | cc = MIN(cc, nclients); 362 | for (c = fc; c < fc + cc; c++) { 363 | dc.w = width / cc; 364 | if (c == sel) { 365 | col = dc.sel; 366 | dc.w += width % cc; 367 | } else { 368 | col = clients[c]->urgent ? dc.urg : dc.norm; 369 | } 370 | drawtext(clients[c]->name, col); 371 | dc.x += dc.w; 372 | clients[c]->tabx = dc.x; 373 | } 374 | XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 375 | XSync(dpy, False); 376 | } 377 | 378 | void 379 | drawtext(const char *text, XftColor col[ColLast]) 380 | { 381 | int i, j, x, y, h, len, olen; 382 | char buf[256]; 383 | XftDraw *d; 384 | XRectangle r = { dc.x, dc.y, dc.w, dc.h }; 385 | 386 | XSetForeground(dpy, dc.gc, col[ColBG].pixel); 387 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); 388 | if (!text) 389 | return; 390 | 391 | olen = strlen(text); 392 | h = dc.font.ascent + dc.font.descent; 393 | y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; 394 | x = dc.x + (h / 2); 395 | 396 | /* shorten text if necessary */ 397 | for (len = MIN(olen, sizeof(buf)); 398 | len && textnw(text, len) > dc.w - h; len--); 399 | 400 | if (!len) 401 | return; 402 | 403 | memcpy(buf, text, len); 404 | if (len < olen) { 405 | for (i = len, j = strlen(titletrim); j && i; 406 | buf[--i] = titletrim[--j]) 407 | ; 408 | } 409 | 410 | d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); 411 | XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); 412 | XftDrawDestroy(d); 413 | } 414 | 415 | void * 416 | ecalloc(size_t n, size_t size) 417 | { 418 | void *p; 419 | 420 | if (!(p = calloc(n, size))) 421 | die("%s: cannot calloc\n", argv0); 422 | return p; 423 | } 424 | 425 | void * 426 | erealloc(void *o, size_t size) 427 | { 428 | void *p; 429 | 430 | if (!(p = realloc(o, size))) 431 | die("%s: cannot realloc\n", argv0); 432 | return p; 433 | } 434 | 435 | void 436 | expose(const XEvent *e) 437 | { 438 | const XExposeEvent *ev = &e->xexpose; 439 | 440 | if (ev->count == 0 && win == ev->window) 441 | drawbar(); 442 | } 443 | 444 | void 445 | focus(int c) 446 | { 447 | char buf[BUFSIZ] = "tabbed-"VERSION" ::"; 448 | size_t i, n; 449 | XWMHints* wmh; 450 | 451 | /* If c, sel and clients are -1, raise tabbed-win itself */ 452 | if (nclients == 0) { 453 | cmd[cmd_append_pos] = NULL; 454 | for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) 455 | n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); 456 | 457 | xsettitle(win, buf); 458 | XRaiseWindow(dpy, win); 459 | 460 | return; 461 | } 462 | 463 | if (c < 0 || c >= nclients) 464 | return; 465 | 466 | resize(c, ww, wh - bh); 467 | XRaiseWindow(dpy, clients[c]->win); 468 | XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime); 469 | sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); 470 | sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); 471 | xsettitle(win, clients[c]->name); 472 | 473 | if (sel != c) { 474 | lastsel = sel; 475 | sel = c; 476 | } 477 | 478 | if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->win))) { 479 | wmh->flags &= ~XUrgencyHint; 480 | XSetWMHints(dpy, clients[c]->win, wmh); 481 | clients[c]->urgent = False; 482 | XFree(wmh); 483 | } 484 | 485 | drawbar(); 486 | XSync(dpy, False); 487 | } 488 | 489 | void 490 | focusin(const XEvent *e) 491 | { 492 | const XFocusChangeEvent *ev = &e->xfocus; 493 | int dummy; 494 | Window focused; 495 | 496 | if (ev->mode != NotifyUngrab) { 497 | XGetInputFocus(dpy, &focused, &dummy); 498 | if (focused == win) 499 | focus(sel); 500 | } 501 | } 502 | 503 | void 504 | focusonce(const Arg *arg) 505 | { 506 | nextfocus = True; 507 | } 508 | 509 | void 510 | focusurgent(const Arg *arg) 511 | { 512 | int c; 513 | 514 | if (sel < 0) 515 | return; 516 | 517 | for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients) { 518 | if (clients[c]->urgent) { 519 | focus(c); 520 | return; 521 | } 522 | } 523 | } 524 | 525 | void 526 | fullscreen(const Arg *arg) 527 | { 528 | XEvent e; 529 | 530 | e.type = ClientMessage; 531 | e.xclient.window = win; 532 | e.xclient.message_type = wmatom[WMState]; 533 | e.xclient.format = 32; 534 | e.xclient.data.l[0] = 2; 535 | e.xclient.data.l[1] = wmatom[WMFullscreen]; 536 | e.xclient.data.l[2] = 0; 537 | XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); 538 | } 539 | 540 | char * 541 | getatom(int a) 542 | { 543 | static char buf[BUFSIZ]; 544 | Atom adummy; 545 | int idummy; 546 | unsigned long ldummy; 547 | unsigned char *p = NULL; 548 | 549 | XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_STRING, 550 | &adummy, &idummy, &ldummy, &ldummy, &p); 551 | if (p) 552 | strncpy(buf, (char *)p, LENGTH(buf)-1); 553 | else 554 | buf[0] = '\0'; 555 | XFree(p); 556 | 557 | return buf; 558 | } 559 | 560 | int 561 | getclient(Window w) 562 | { 563 | int i; 564 | 565 | for (i = 0; i < nclients; i++) { 566 | if (clients[i]->win == w) 567 | return i; 568 | } 569 | 570 | return -1; 571 | } 572 | 573 | XftColor 574 | getcolor(const char *colstr) 575 | { 576 | XftColor color; 577 | 578 | if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), colstr, &color)) 579 | die("%s: cannot allocate color '%s'\n", argv0, colstr); 580 | 581 | return color; 582 | } 583 | 584 | int 585 | getfirsttab(void) 586 | { 587 | int cc, ret; 588 | 589 | if (sel < 0) 590 | return 0; 591 | 592 | cc = ww / tabwidth; 593 | if (nclients > cc) 594 | cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 595 | 596 | ret = sel - cc / 2 + (cc + 1) % 2; 597 | return ret < 0 ? 0 : 598 | ret + cc > nclients ? MAX(0, nclients - cc) : 599 | ret; 600 | } 601 | 602 | Bool 603 | gettextprop(Window w, Atom atom, char *text, unsigned int size) 604 | { 605 | char **list = NULL; 606 | int n; 607 | XTextProperty name; 608 | 609 | if (!text || size == 0) 610 | return False; 611 | 612 | text[0] = '\0'; 613 | XGetTextProperty(dpy, w, &name, atom); 614 | if (!name.nitems) 615 | return False; 616 | 617 | if (name.encoding == XA_STRING) { 618 | strncpy(text, (char *)name.value, size - 1); 619 | } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success 620 | && n > 0 && *list) { 621 | strncpy(text, *list, size - 1); 622 | XFreeStringList(list); 623 | } 624 | text[size - 1] = '\0'; 625 | XFree(name.value); 626 | 627 | return True; 628 | } 629 | 630 | void 631 | initfont(const char *fontstr) 632 | { 633 | if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr)) 634 | && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed"))) 635 | die("error, cannot load font: '%s'\n", fontstr); 636 | 637 | dc.font.ascent = dc.font.xfont->ascent; 638 | dc.font.descent = dc.font.xfont->descent; 639 | dc.font.height = dc.font.ascent + dc.font.descent; 640 | } 641 | 642 | Bool 643 | isprotodel(int c) 644 | { 645 | int i, n; 646 | Atom *protocols; 647 | Bool ret = False; 648 | 649 | if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) { 650 | for (i = 0; !ret && i < n; i++) { 651 | if (protocols[i] == wmatom[WMDelete]) 652 | ret = True; 653 | } 654 | XFree(protocols); 655 | } 656 | 657 | return ret; 658 | } 659 | 660 | void 661 | keypress(const XEvent *e) 662 | { 663 | const XKeyEvent *ev = &e->xkey; 664 | unsigned int i; 665 | KeySym keysym; 666 | 667 | keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 668 | for (i = 0; i < LENGTH(keys); i++) { 669 | if (keysym == keys[i].keysym && 670 | CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && 671 | keys[i].func) 672 | keys[i].func(&(keys[i].arg)); 673 | } 674 | } 675 | 676 | void 677 | killclient(const Arg *arg) 678 | { 679 | XEvent ev; 680 | 681 | if (sel < 0) 682 | return; 683 | 684 | if (isprotodel(sel) && !clients[sel]->closed) { 685 | ev.type = ClientMessage; 686 | ev.xclient.window = clients[sel]->win; 687 | ev.xclient.message_type = wmatom[WMProtocols]; 688 | ev.xclient.format = 32; 689 | ev.xclient.data.l[0] = wmatom[WMDelete]; 690 | ev.xclient.data.l[1] = CurrentTime; 691 | XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &ev); 692 | clients[sel]->closed = True; 693 | } else { 694 | XKillClient(dpy, clients[sel]->win); 695 | } 696 | } 697 | 698 | void 699 | manage(Window w) 700 | { 701 | updatenumlockmask(); 702 | { 703 | int i, j, nextpos; 704 | unsigned int modifiers[] = { 0, LockMask, numlockmask, 705 | numlockmask | LockMask }; 706 | KeyCode code; 707 | Client *c; 708 | XEvent e; 709 | 710 | XWithdrawWindow(dpy, w, 0); 711 | XReparentWindow(dpy, w, win, 0, bh); 712 | XSelectInput(dpy, w, PropertyChangeMask | 713 | StructureNotifyMask | EnterWindowMask); 714 | XSync(dpy, False); 715 | 716 | for (i = 0; i < LENGTH(keys); i++) { 717 | if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { 718 | for (j = 0; j < LENGTH(modifiers); j++) { 719 | XGrabKey(dpy, code, keys[i].mod | 720 | modifiers[j], w, True, 721 | GrabModeAsync, GrabModeAsync); 722 | } 723 | } 724 | } 725 | 726 | c = ecalloc(1, sizeof *c); 727 | c->win = w; 728 | 729 | nclients++; 730 | clients = erealloc(clients, sizeof(Client *) * nclients); 731 | 732 | if(npisrelative) { 733 | nextpos = sel + newposition; 734 | } else { 735 | if (newposition < 0) 736 | nextpos = nclients - newposition; 737 | else 738 | nextpos = newposition; 739 | } 740 | if (nextpos >= nclients) 741 | nextpos = nclients - 1; 742 | if (nextpos < 0) 743 | nextpos = 0; 744 | 745 | if (nclients > 1 && nextpos < nclients - 1) 746 | memmove(&clients[nextpos + 1], &clients[nextpos], 747 | sizeof(Client *) * (nclients - nextpos - 1)); 748 | 749 | clients[nextpos] = c; 750 | updatetitle(nextpos); 751 | 752 | XLowerWindow(dpy, w); 753 | XMapWindow(dpy, w); 754 | 755 | e.xclient.window = w; 756 | e.xclient.type = ClientMessage; 757 | e.xclient.message_type = wmatom[XEmbed]; 758 | e.xclient.format = 32; 759 | e.xclient.data.l[0] = CurrentTime; 760 | e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; 761 | e.xclient.data.l[2] = 0; 762 | e.xclient.data.l[3] = win; 763 | e.xclient.data.l[4] = 0; 764 | XSendEvent(dpy, root, False, NoEventMask, &e); 765 | 766 | XSync(dpy, False); 767 | 768 | /* Adjust sel before focus does set it to lastsel. */ 769 | if (sel >= nextpos) 770 | sel++; 771 | focus(nextfocus ? nextpos : 772 | sel < 0 ? 0 : 773 | sel); 774 | nextfocus = foreground; 775 | } 776 | } 777 | 778 | void 779 | maprequest(const XEvent *e) 780 | { 781 | const XMapRequestEvent *ev = &e->xmaprequest; 782 | 783 | if (getclient(ev->window) < 0) 784 | manage(ev->window); 785 | } 786 | 787 | void 788 | move(const Arg *arg) 789 | { 790 | if (arg->i >= 0 && arg->i < nclients) 791 | focus(arg->i); 792 | } 793 | 794 | void 795 | movetab(const Arg *arg) 796 | { 797 | int c; 798 | Client *new; 799 | 800 | if (sel < 0) 801 | return; 802 | 803 | c = (sel + arg->i) % nclients; 804 | if (c < 0) 805 | c += nclients; 806 | 807 | if (c == sel) 808 | return; 809 | 810 | new = clients[sel]; 811 | if (sel < c) 812 | memmove(&clients[sel], &clients[sel+1], 813 | sizeof(Client *) * (c - sel)); 814 | else 815 | memmove(&clients[c+1], &clients[c], 816 | sizeof(Client *) * (sel - c)); 817 | clients[c] = new; 818 | sel = c; 819 | 820 | drawbar(); 821 | } 822 | 823 | void 824 | propertynotify(const XEvent *e) 825 | { 826 | const XPropertyEvent *ev = &e->xproperty; 827 | XWMHints *wmh; 828 | int c; 829 | char* selection = NULL; 830 | Arg arg; 831 | 832 | if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelectTab]) { 833 | selection = getatom(WMSelectTab); 834 | if (!strncmp(selection, "0x", 2)) { 835 | arg.i = getclient(strtoul(selection, NULL, 0)); 836 | move(&arg); 837 | } else { 838 | cmd[cmd_append_pos] = selection; 839 | arg.v = cmd; 840 | spawn(&arg); 841 | } 842 | } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HINTS && 843 | (c = getclient(ev->window)) > -1 && 844 | (wmh = XGetWMHints(dpy, clients[c]->win))) { 845 | if (wmh->flags & XUrgencyHint) { 846 | XFree(wmh); 847 | wmh = XGetWMHints(dpy, win); 848 | if (c != sel) { 849 | if (urgentswitch && wmh && 850 | !(wmh->flags & XUrgencyHint)) { 851 | /* only switch, if tabbed was focused 852 | * since last urgency hint if WMHints 853 | * could not be received, 854 | * default to no switch */ 855 | focus(c); 856 | } else { 857 | /* if no switch should be performed, 858 | * mark tab as urgent */ 859 | clients[c]->urgent = True; 860 | drawbar(); 861 | } 862 | } 863 | if (wmh && !(wmh->flags & XUrgencyHint)) { 864 | /* update tabbed urgency hint 865 | * if not set already */ 866 | wmh->flags |= XUrgencyHint; 867 | XSetWMHints(dpy, win, wmh); 868 | } 869 | } 870 | XFree(wmh); 871 | } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME && 872 | (c = getclient(ev->window)) > -1) { 873 | updatetitle(c); 874 | } 875 | } 876 | 877 | void 878 | resize(int c, int w, int h) 879 | { 880 | XConfigureEvent ce; 881 | XWindowChanges wc; 882 | 883 | ce.x = 0; 884 | ce.y = wc.y = bh; 885 | ce.width = wc.width = w; 886 | ce.height = wc.height = h; 887 | ce.type = ConfigureNotify; 888 | ce.display = dpy; 889 | ce.event = clients[c]->win; 890 | ce.window = clients[c]->win; 891 | ce.above = None; 892 | ce.override_redirect = False; 893 | ce.border_width = 0; 894 | 895 | XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight, &wc); 896 | XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask, 897 | (XEvent *)&ce); 898 | } 899 | 900 | void 901 | rotate(const Arg *arg) 902 | { 903 | int nsel = -1; 904 | 905 | if (sel < 0) 906 | return; 907 | 908 | if (arg->i == 0) { 909 | if (lastsel > -1) 910 | focus(lastsel); 911 | } else if (sel > -1) { 912 | /* Rotating in an arg->i step around the clients. */ 913 | nsel = sel + arg->i; 914 | while (nsel >= nclients) 915 | nsel -= nclients; 916 | while (nsel < 0) 917 | nsel += nclients; 918 | focus(nsel); 919 | } 920 | } 921 | 922 | void 923 | run(void) 924 | { 925 | XEvent ev; 926 | 927 | /* main event loop */ 928 | XSync(dpy, False); 929 | drawbar(); 930 | if (doinitspawn == True) 931 | spawn(NULL); 932 | 933 | while (running) { 934 | XNextEvent(dpy, &ev); 935 | if (handler[ev.type]) 936 | (handler[ev.type])(&ev); /* call handler */ 937 | } 938 | } 939 | 940 | void 941 | sendxembed(int c, long msg, long detail, long d1, long d2) 942 | { 943 | XEvent e = { 0 }; 944 | 945 | e.xclient.window = clients[c]->win; 946 | e.xclient.type = ClientMessage; 947 | e.xclient.message_type = wmatom[XEmbed]; 948 | e.xclient.format = 32; 949 | e.xclient.data.l[0] = CurrentTime; 950 | e.xclient.data.l[1] = msg; 951 | e.xclient.data.l[2] = detail; 952 | e.xclient.data.l[3] = d1; 953 | e.xclient.data.l[4] = d2; 954 | XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e); 955 | } 956 | 957 | void 958 | setcmd(int argc, char *argv[], int replace) 959 | { 960 | int i; 961 | 962 | cmd = ecalloc(argc + 3, sizeof(*cmd)); 963 | if (argc == 0) 964 | return; 965 | for (i = 0; i < argc; i++) 966 | cmd[i] = argv[i]; 967 | cmd[replace > 0 ? replace : argc] = winid; 968 | cmd_append_pos = argc + !replace; 969 | cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL; 970 | } 971 | 972 | void 973 | setup(void) 974 | { 975 | int bitm, tx, ty, tw, th, dh, dw, isfixed; 976 | XWMHints *wmh; 977 | XClassHint class_hint; 978 | XSizeHints *size_hint; 979 | 980 | /* clean up any zombies immediately */ 981 | sigchld(0); 982 | 983 | /* init screen */ 984 | screen = DefaultScreen(dpy); 985 | root = RootWindow(dpy, screen); 986 | initfont(font); 987 | bh = dc.h = dc.font.height + 2; 988 | 989 | /* init atoms */ 990 | wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 991 | wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 992 | False); 993 | wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 994 | wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 995 | wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); 996 | wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 997 | wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); 998 | 999 | /* init appearance */ 1000 | wx = 0; 1001 | wy = 0; 1002 | //ww = 800; 1003 | //wh = 600; 1004 | ww = 3200; 1005 | wh = 1800; 1006 | isfixed = 0; 1007 | 1008 | if (geometry) { 1009 | tx = ty = tw = th = 0; 1010 | bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&tw, 1011 | (unsigned *)&th); 1012 | if (bitm & XValue) 1013 | wx = tx; 1014 | if (bitm & YValue) 1015 | wy = ty; 1016 | if (bitm & WidthValue) 1017 | ww = tw; 1018 | if (bitm & HeightValue) 1019 | wh = th; 1020 | if (bitm & XNegative && wx == 0) 1021 | wx = -1; 1022 | if (bitm & YNegative && wy == 0) 1023 | wy = -1; 1024 | if (bitm & (HeightValue | WidthValue)) 1025 | isfixed = 1; 1026 | 1027 | dw = DisplayWidth(dpy, screen); 1028 | dh = DisplayHeight(dpy, screen); 1029 | if (wx < 0) 1030 | wx = dw + wx - ww - 1; 1031 | if (wy < 0) 1032 | wy = dh + wy - wh - 1; 1033 | } 1034 | 1035 | dc.norm[ColBG] = getcolor(normbgcolor); 1036 | dc.norm[ColFG] = getcolor(normfgcolor); 1037 | dc.sel[ColBG] = getcolor(selbgcolor); 1038 | dc.sel[ColFG] = getcolor(selfgcolor); 1039 | dc.urg[ColBG] = getcolor(urgbgcolor); 1040 | dc.urg[ColFG] = getcolor(urgfgcolor); 1041 | dc.drawable = XCreatePixmap(dpy, root, ww, wh, 1042 | DefaultDepth(dpy, screen)); 1043 | dc.gc = XCreateGC(dpy, root, 0, 0); 1044 | 1045 | win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, 1046 | dc.norm[ColFG].pixel, dc.norm[ColBG].pixel); 1047 | XMapRaised(dpy, win); 1048 | XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | 1049 | ButtonPressMask | ExposureMask | KeyPressMask | 1050 | PropertyChangeMask | StructureNotifyMask | 1051 | SubstructureRedirectMask); 1052 | xerrorxlib = XSetErrorHandler(xerror); 1053 | 1054 | class_hint.res_name = wmname; 1055 | class_hint.res_class = "tabbed"; 1056 | XSetClassHint(dpy, win, &class_hint); 1057 | 1058 | size_hint = XAllocSizeHints(); 1059 | if (!isfixed) { 1060 | size_hint->flags = PSize | PMinSize; 1061 | size_hint->height = wh; 1062 | size_hint->width = ww; 1063 | size_hint->min_height = bh + 1; 1064 | } else { 1065 | size_hint->flags = PMaxSize | PMinSize; 1066 | size_hint->min_width = size_hint->max_width = ww; 1067 | size_hint->min_height = size_hint->max_height = wh; 1068 | } 1069 | wmh = XAllocWMHints(); 1070 | XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, NULL); 1071 | XFree(size_hint); 1072 | XFree(wmh); 1073 | 1074 | XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); 1075 | 1076 | snprintf(winid, sizeof(winid), "%lu", win); 1077 | setenv("XEMBED", winid, 1); 1078 | 1079 | nextfocus = foreground; 1080 | focus(-1); 1081 | } 1082 | 1083 | void 1084 | sigchld(int unused) 1085 | { 1086 | if (signal(SIGCHLD, sigchld) == SIG_ERR) 1087 | die("%s: cannot install SIGCHLD handler", argv0); 1088 | 1089 | while (0 < waitpid(-1, NULL, WNOHANG)); 1090 | } 1091 | 1092 | void 1093 | spawn(const Arg *arg) 1094 | { 1095 | if (fork() == 0) { 1096 | if(dpy) 1097 | close(ConnectionNumber(dpy)); 1098 | 1099 | setsid(); 1100 | if (arg && arg->v) { 1101 | execvp(((char **)arg->v)[0], (char **)arg->v); 1102 | fprintf(stderr, "%s: execvp %s", argv0, 1103 | ((char **)arg->v)[0]); 1104 | } else { 1105 | cmd[cmd_append_pos] = NULL; 1106 | execvp(cmd[0], cmd); 1107 | fprintf(stderr, "%s: execvp %s", argv0, cmd[0]); 1108 | } 1109 | perror(" failed"); 1110 | exit(0); 1111 | } 1112 | } 1113 | 1114 | int 1115 | textnw(const char *text, unsigned int len) 1116 | { 1117 | XGlyphInfo ext; 1118 | XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &ext); 1119 | return ext.xOff; 1120 | } 1121 | 1122 | void 1123 | toggle(const Arg *arg) 1124 | { 1125 | *(Bool*) arg->v = !*(Bool*) arg->v; 1126 | } 1127 | 1128 | void 1129 | unmanage(int c) 1130 | { 1131 | if (c < 0 || c >= nclients) { 1132 | drawbar(); 1133 | XSync(dpy, False); 1134 | return; 1135 | } 1136 | 1137 | if (!nclients) 1138 | return; 1139 | 1140 | if (c == 0) { 1141 | /* First client. */ 1142 | nclients--; 1143 | free(clients[0]); 1144 | memmove(&clients[0], &clients[1], sizeof(Client *) * nclients); 1145 | } else if (c == nclients - 1) { 1146 | /* Last client. */ 1147 | nclients--; 1148 | free(clients[c]); 1149 | clients = erealloc(clients, sizeof(Client *) * nclients); 1150 | } else { 1151 | /* Somewhere inbetween. */ 1152 | free(clients[c]); 1153 | memmove(&clients[c], &clients[c+1], 1154 | sizeof(Client *) * (nclients - (c + 1))); 1155 | nclients--; 1156 | } 1157 | 1158 | if (nclients <= 0) { 1159 | lastsel = sel = -1; 1160 | 1161 | if (closelastclient) 1162 | running = False; 1163 | else if (fillagain && running) 1164 | spawn(NULL); 1165 | } else { 1166 | if (lastsel >= nclients) 1167 | lastsel = nclients - 1; 1168 | else if (lastsel > c) 1169 | lastsel--; 1170 | 1171 | if (c == sel && lastsel >= 0) { 1172 | focus(lastsel); 1173 | } else { 1174 | if (sel > c) 1175 | sel--; 1176 | if (sel >= nclients) 1177 | sel = nclients - 1; 1178 | 1179 | focus(sel); 1180 | } 1181 | } 1182 | 1183 | drawbar(); 1184 | XSync(dpy, False); 1185 | } 1186 | 1187 | void 1188 | unmapnotify(const XEvent *e) 1189 | { 1190 | const XUnmapEvent *ev = &e->xunmap; 1191 | int c; 1192 | 1193 | if ((c = getclient(ev->window)) > -1) 1194 | unmanage(c); 1195 | } 1196 | 1197 | void 1198 | updatenumlockmask(void) 1199 | { 1200 | unsigned int i, j; 1201 | XModifierKeymap *modmap; 1202 | 1203 | numlockmask = 0; 1204 | modmap = XGetModifierMapping(dpy); 1205 | for (i = 0; i < 8; i++) { 1206 | for (j = 0; j < modmap->max_keypermod; j++) { 1207 | if (modmap->modifiermap[i * modmap->max_keypermod + j] 1208 | == XKeysymToKeycode(dpy, XK_Num_Lock)) 1209 | numlockmask = (1 << i); 1210 | } 1211 | } 1212 | XFreeModifiermap(modmap); 1213 | } 1214 | 1215 | void 1216 | updatetitle(int c) 1217 | { 1218 | if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->name, 1219 | sizeof(clients[c]->name))) 1220 | gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->name, 1221 | sizeof(clients[c]->name)); 1222 | if (sel == c) 1223 | xsettitle(win, clients[c]->name); 1224 | drawbar(); 1225 | } 1226 | 1227 | /* There's no way to check accesses to destroyed windows, thus those cases are 1228 | * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 1229 | * default error handler, which may call exit. */ 1230 | int 1231 | xerror(Display *dpy, XErrorEvent *ee) 1232 | { 1233 | if (ee->error_code == BadWindow 1234 | || (ee->request_code == X_SetInputFocus && 1235 | ee->error_code == BadMatch) 1236 | || (ee->request_code == X_PolyText8 && 1237 | ee->error_code == BadDrawable) 1238 | || (ee->request_code == X_PolyFillRectangle && 1239 | ee->error_code == BadDrawable) 1240 | || (ee->request_code == X_PolySegment && 1241 | ee->error_code == BadDrawable) 1242 | || (ee->request_code == X_ConfigureWindow && 1243 | ee->error_code == BadMatch) 1244 | || (ee->request_code == X_GrabButton && 1245 | ee->error_code == BadAccess) 1246 | || (ee->request_code == X_GrabKey && 1247 | ee->error_code == BadAccess) 1248 | || (ee->request_code == X_CopyArea && 1249 | ee->error_code == BadDrawable)) 1250 | return 0; 1251 | 1252 | fprintf(stderr, "%s: fatal error: request code=%d, error code=%d\n", 1253 | argv0, ee->request_code, ee->error_code); 1254 | return xerrorxlib(dpy, ee); /* may call exit */ 1255 | } 1256 | 1257 | void 1258 | xsettitle(Window w, const char *str) 1259 | { 1260 | XTextProperty xtp; 1261 | 1262 | if (XmbTextListToTextProperty(dpy, (char **)&str, 1, 1263 | XCompoundTextStyle, &xtp) == Success) { 1264 | XSetTextProperty(dpy, w, &xtp, wmatom[WMName]); 1265 | XSetTextProperty(dpy, w, &xtp, XA_WM_NAME); 1266 | XFree(xtp.value); 1267 | } 1268 | } 1269 | 1270 | void 1271 | usage(void) 1272 | { 1273 | die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" 1274 | " [-r narg] [-o color] [-O color] [-t color] [-T color]\n" 1275 | " [-u color] [-U color] command...\n", argv0); 1276 | } 1277 | 1278 | int 1279 | main(int argc, char *argv[]) 1280 | { 1281 | Bool detach = False; 1282 | int replace = 0; 1283 | char *pstr; 1284 | 1285 | ARGBEGIN { 1286 | case 'c': 1287 | closelastclient = True; 1288 | fillagain = False; 1289 | break; 1290 | case 'd': 1291 | detach = True; 1292 | break; 1293 | case 'f': 1294 | fillagain = True; 1295 | break; 1296 | case 'g': 1297 | geometry = EARGF(usage()); 1298 | break; 1299 | case 'k': 1300 | killclientsfirst = True; 1301 | break; 1302 | case 'n': 1303 | wmname = EARGF(usage()); 1304 | break; 1305 | case 'O': 1306 | normfgcolor = EARGF(usage()); 1307 | break; 1308 | case 'o': 1309 | normbgcolor = EARGF(usage()); 1310 | break; 1311 | case 'p': 1312 | pstr = EARGF(usage()); 1313 | if (pstr[0] == 's') { 1314 | npisrelative = True; 1315 | newposition = atoi(&pstr[1]); 1316 | } else { 1317 | newposition = atoi(pstr); 1318 | } 1319 | break; 1320 | case 'r': 1321 | replace = atoi(EARGF(usage())); 1322 | break; 1323 | case 's': 1324 | doinitspawn = False; 1325 | break; 1326 | case 'T': 1327 | selfgcolor = EARGF(usage()); 1328 | break; 1329 | case 't': 1330 | selbgcolor = EARGF(usage()); 1331 | break; 1332 | case 'U': 1333 | urgfgcolor = EARGF(usage()); 1334 | break; 1335 | case 'u': 1336 | urgbgcolor = EARGF(usage()); 1337 | break; 1338 | case 'v': 1339 | die("tabbed-"VERSION", © 2009-2016 tabbed engineers, " 1340 | "see LICENSE for details.\n"); 1341 | break; 1342 | default: 1343 | usage(); 1344 | break; 1345 | } ARGEND; 1346 | 1347 | if (argc < 1) { 1348 | doinitspawn = False; 1349 | fillagain = False; 1350 | } 1351 | 1352 | setcmd(argc, argv, replace); 1353 | 1354 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 1355 | fprintf(stderr, "%s: no locale support\n", argv0); 1356 | if (!(dpy = XOpenDisplay(NULL))) 1357 | die("%s: cannot open display\n", argv0); 1358 | 1359 | setup(); 1360 | printf("0x%lx\n", win); 1361 | fflush(NULL); 1362 | 1363 | if (detach) { 1364 | if (fork() == 0) { 1365 | fclose(stdout); 1366 | } else { 1367 | if (dpy) 1368 | close(ConnectionNumber(dpy)); 1369 | return EXIT_SUCCESS; 1370 | } 1371 | } 1372 | 1373 | run(); 1374 | cleanup(); 1375 | XCloseDisplay(dpy); 1376 | 1377 | return EXIT_SUCCESS; 1378 | } 1379 | -------------------------------------------------------------------------------- /terminfo.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/CuteXterm/5270ff79e350514ea7f8ee18230cfca1534d57ce/terminfo.tgz --------------------------------------------------------------------------------