├── .gitignore
├── screenshots
└── popover.png
├── dwt.gresources.xml
├── dwt.desktop
├── dwt-settings.h
├── meson.build
├── .ycm_extra_conf.py
├── COPYING
├── themes
├── thayer.h
├── wombat.h
├── molokai.h
├── monokai.h
├── zenburn.h
├── solarized.h
├── jellybeans.h
└── linux.h
├── menus.xml
├── README.rst
├── tools
└── convert-theme-xrdb
├── tests
└── test-settings.c
├── dg-settings.h
├── dwt.rst
├── dwt-settings.c
├── dwt.1
├── dg-settings.c
└── dwt.c
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | dwt
3 | *.sw[op]
4 | *.py[co]
5 | *.gresources.[hc]
6 | tests/test-settings
7 |
--------------------------------------------------------------------------------
/screenshots/popover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aperezdc/dwt/master/screenshots/popover.png
--------------------------------------------------------------------------------
/dwt.gresources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | menus.xml
5 |
6 |
7 |
9 |
--------------------------------------------------------------------------------
/dwt.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Hidden=false
3 | Name=dwt
4 | Exec=dwt
5 | TryExec=dwt
6 | Icon=utilities-terminal
7 | Comment=Use the command line
8 | Type=Application
9 | NoDisplay=false
10 | StartupNotify=true
11 | StartupWMClass=Dwt
12 | Categories=GNOME;GTK;Utility;TerminalEmulator;
13 | Terminal=false
14 | Actions=New
15 |
16 | [Desktop Action New]
17 | Name=New Terminal
18 | Exec=dwt
19 |
--------------------------------------------------------------------------------
/dwt-settings.h:
--------------------------------------------------------------------------------
1 | /*
2 | * dwt-settings.h
3 | * Copyright (C) 2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef DWT_SETTINGS_H
9 | #define DWT_SETTINGS_H
10 |
11 | #include
12 |
13 | G_BEGIN_DECLS
14 |
15 | typedef struct _DwtSettings DwtSettings;
16 |
17 | DwtSettings* dwt_settings_get_instance (void);
18 |
19 | G_END_DECLS
20 |
21 | #endif /* !DWT_SETTINGS_H */
22 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('dwt', 'c',
2 | version: '0.6.1',
3 | default_options: [
4 | 'buildtype=debugoptimized',
5 | 'c_std=c11',
6 | 'b_lto=true',
7 | ]
8 | )
9 |
10 | gnome = import('gnome')
11 |
12 | executable('dwt',
13 | 'dwt.c',
14 | 'dwt-settings.c',
15 | 'dg-settings.c',
16 | gnome.compile_resources('dwt.gresources', 'dwt.gresources.xml'),
17 | dependencies: dependency('vte-2.91', version: '>=0.50'),
18 | install: true,
19 | )
20 |
21 | install_man('dwt.1')
22 |
23 | install_data('dwt.desktop',
24 | install_dir: join_paths(get_option('datadir'), 'applications'))
25 |
--------------------------------------------------------------------------------
/.ycm_extra_conf.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # vim:fenc=utf-8
4 | #
5 | # Copyright © 2014 Adrian Perez
6 | #
7 | # Distributed under terms of the MIT license.
8 |
9 | from subprocess import check_output
10 | from shlex import split as sh_split
11 |
12 | def FlagsForFile(path, **kwarg):
13 | flags = sh_split(check_output(["make", "print-flags"]))
14 | flags.extend(("-Qunused-arguments",
15 | "-DDWT_USE_POPOVER=TRUE",
16 | "-DDWT_USE_OVERLAY=TRUE"))
17 | return { 'flags': flags, 'do_cache': True }
18 |
19 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014 by Adrian Perez
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/themes/thayer.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_THAYER_H
2 | #define THEME_THAYER_H 1
3 | {
4 | .name = "thayer",
5 | .fg = { 0.972549, 0.972549, 0.972549, 1.000000 }, /* #f8f8f8 */
6 | .bg = { 0.105882, 0.113725, 0.117647, 1.000000 }, /* #1b1d1e */
7 | .colors = {
8 | { 0.105882, 0.113725, 0.117647, 1.000000 }, /* #1b1d1e */
9 | { 0.976471, 0.149020, 0.447059, 1.000000 }, /* #f92672 */
10 | { 0.301961, 0.972549, 0.250980, 1.000000 }, /* #4df840 */
11 | { 0.956863, 0.992157, 0.133333, 1.000000 }, /* #f4fd22 */
12 | { 0.152941, 0.341176, 0.839216, 1.000000 }, /* #2757d6 */
13 | { 0.549020, 0.329412, 0.996078, 1.000000 }, /* #8c54fe */
14 | { 0.219608, 0.784314, 0.709804, 1.000000 }, /* #38c8b5 */
15 | { 0.800000, 0.800000, 0.776471, 1.000000 }, /* #ccccc6 */
16 | { 0.313725, 0.325490, 0.329412, 1.000000 }, /* #505354 */
17 | { 1.000000, 0.349020, 0.584314, 1.000000 }, /* #ff5995 */
18 | { 0.713725, 0.890196, 0.329412, 1.000000 }, /* #b6e354 */
19 | { 0.996078, 0.929412, 0.423529, 1.000000 }, /* #feed6c */
20 | { 0.247059, 0.470588, 1.000000, 1.000000 }, /* #3f78ff */
21 | { 0.619608, 0.435294, 0.996078, 1.000000 }, /* #9e6ffe */
22 | { 0.137255, 0.811765, 0.835294, 1.000000 }, /* #23cfd5 */
23 | { 0.972549, 0.972549, 0.949020, 1.000000 }, /* #f8f8f2 */
24 | },
25 | },
26 | #endif /* THEME_THAYER */
27 |
--------------------------------------------------------------------------------
/themes/wombat.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_WOMBAT_H
2 | #define THEME_WOMBAT_H 1
3 | {
4 | .name = "wombat",
5 | .fg = { 0.870588, 0.854902, 0.811765, 1.000000 }, /* #dedacf */
6 | .bg = { 0.090196, 0.090196, 0.090196, 1.000000 }, /* #171717 */
7 | .colors = {
8 | { 0.000000, 0.000000, 0.000000, 1.000000 }, /* #000000 */
9 | { 1.000000, 0.380392, 0.352941, 1.000000 }, /* #ff615a */
10 | { 0.694118, 0.913725, 0.411765, 1.000000 }, /* #b1e969 */
11 | { 0.921569, 0.850980, 0.611765, 1.000000 }, /* #ebd99c */
12 | { 0.364706, 0.662745, 0.964706, 1.000000 }, /* #5da9f6 */
13 | { 0.909804, 0.415686, 1.000000, 1.000000 }, /* #e86aff */
14 | { 0.509804, 1.000000, 0.968627, 1.000000 }, /* #82fff7 */
15 | { 0.870588, 0.854902, 0.811765, 1.000000 }, /* #dedacf */
16 | { 0.192157, 0.192157, 0.192157, 1.000000 }, /* #313131 */
17 | { 0.960784, 0.549020, 0.501961, 1.000000 }, /* #f58c80 */
18 | { 0.866667, 0.972549, 0.560784, 1.000000 }, /* #ddf88f */
19 | { 0.933333, 0.898039, 0.698039, 1.000000 }, /* #eee5b2 */
20 | { 0.647059, 0.780392, 1.000000, 1.000000 }, /* #a5c7ff */
21 | { 0.866667, 0.666667, 1.000000, 1.000000 }, /* #ddaaff */
22 | { 0.717647, 1.000000, 0.976471, 1.000000 }, /* #b7fff9 */
23 | { 1.000000, 1.000000, 1.000000, 1.000000 }, /* #ffffff */
24 | },
25 | },
26 | #endif /* THEME_WOMBAT */
27 |
--------------------------------------------------------------------------------
/themes/molokai.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_MOLOKAI_H
2 | #define THEME_MOLOKAI_H 1
3 | {
4 | .name = "molokai",
5 | .fg = { 0.733333, 0.733333, 0.733333, 1.000000 }, /* #bbbbbb */
6 | .bg = { 0.070588, 0.070588, 0.070588, 1.000000 }, /* #121212 */
7 | .colors = {
8 | { 0.070588, 0.070588, 0.070588, 1.000000 }, /* #121212 */
9 | { 0.980392, 0.145098, 0.450980, 1.000000 }, /* #fa2573 */
10 | { 0.596078, 0.882353, 0.137255, 1.000000 }, /* #98e123 */
11 | { 0.874510, 0.831373, 0.376471, 1.000000 }, /* #dfd460 */
12 | { 0.062745, 0.501961, 0.815686, 1.000000 }, /* #1080d0 */
13 | { 0.529412, 0.000000, 1.000000, 1.000000 }, /* #8700ff */
14 | { 0.262745, 0.658824, 0.815686, 1.000000 }, /* #43a8d0 */
15 | { 0.733333, 0.733333, 0.733333, 1.000000 }, /* #bbbbbb */
16 | { 0.333333, 0.333333, 0.333333, 1.000000 }, /* #555555 */
17 | { 0.964706, 0.400000, 0.615686, 1.000000 }, /* #f6669d */
18 | { 0.694118, 0.878431, 0.372549, 1.000000 }, /* #b1e05f */
19 | { 1.000000, 0.949020, 0.427451, 1.000000 }, /* #fff26d */
20 | { 0.000000, 0.686275, 1.000000, 1.000000 }, /* #00afff */
21 | { 0.686275, 0.529412, 1.000000, 1.000000 }, /* #af87ff */
22 | { 0.317647, 0.807843, 1.000000, 1.000000 }, /* #51ceff */
23 | { 1.000000, 1.000000, 1.000000, 1.000000 }, /* #ffffff */
24 | },
25 | },
26 | #endif /* THEME_MOLOKAI */
27 |
--------------------------------------------------------------------------------
/themes/monokai.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_MONOKAI_H
2 | #define THEME_MONOKAI_H 1
3 | {
4 | .name = "monokai",
5 | .fg = { 0.768627, 0.772549, 0.709804, 1.000000 }, /* #c4c5b5 */
6 | .bg = { 0.101961, 0.101961, 0.101961, 1.000000 }, /* #1a1a1a */
7 | .colors = {
8 | { 0.101961, 0.101961, 0.101961, 1.000000 }, /* #1a1a1a */
9 | { 0.956863, 0.000000, 0.372549, 1.000000 }, /* #f4005f */
10 | { 0.596078, 0.878431, 0.141176, 1.000000 }, /* #98e024 */
11 | { 0.980392, 0.517647, 0.098039, 1.000000 }, /* #fa8419 */
12 | { 0.615686, 0.396078, 1.000000, 1.000000 }, /* #9d65ff */
13 | { 0.956863, 0.000000, 0.372549, 1.000000 }, /* #f4005f */
14 | { 0.345098, 0.819608, 0.921569, 1.000000 }, /* #58d1eb */
15 | { 0.768627, 0.772549, 0.709804, 1.000000 }, /* #c4c5b5 */
16 | { 0.384314, 0.368627, 0.298039, 1.000000 }, /* #625e4c */
17 | { 0.956863, 0.000000, 0.372549, 1.000000 }, /* #f4005f */
18 | { 0.596078, 0.878431, 0.141176, 1.000000 }, /* #98e024 */
19 | { 0.878431, 0.835294, 0.380392, 1.000000 }, /* #e0d561 */
20 | { 0.615686, 0.396078, 1.000000, 1.000000 }, /* #9d65ff */
21 | { 0.956863, 0.000000, 0.372549, 1.000000 }, /* #f4005f */
22 | { 0.345098, 0.819608, 0.921569, 1.000000 }, /* #58d1eb */
23 | { 0.964706, 0.964706, 0.937255, 1.000000 }, /* #f6f6ef */
24 | },
25 | },
26 | #endif /* THEME_MONOKAI */
27 |
--------------------------------------------------------------------------------
/themes/zenburn.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_ZENBURN_H
2 | #define THEME_ZENBURN_H 1
3 | {
4 | .name = "zenburn",
5 | .fg = { 0.862745, 0.862745, 0.800000, 1.000000 }, /* #dcdccc */
6 | .bg = { 0.247059, 0.247059, 0.247059, 1.000000 }, /* #3f3f3f */
7 | .colors = {
8 | { 0.301961, 0.301961, 0.301961, 1.000000 }, /* #4d4d4d */
9 | { 0.439216, 0.313725, 0.313725, 1.000000 }, /* #705050 */
10 | { 0.376471, 0.705882, 0.541176, 1.000000 }, /* #60b48a */
11 | { 0.941176, 0.874510, 0.686275, 1.000000 }, /* #f0dfaf */
12 | { 0.313725, 0.376471, 0.439216, 1.000000 }, /* #506070 */
13 | { 0.862745, 0.549020, 0.764706, 1.000000 }, /* #dc8cc3 */
14 | { 0.549020, 0.815686, 0.827451, 1.000000 }, /* #8cd0d3 */
15 | { 0.862745, 0.862745, 0.800000, 1.000000 }, /* #dcdccc */
16 | { 0.439216, 0.564706, 0.501961, 1.000000 }, /* #709080 */
17 | { 0.862745, 0.639216, 0.639216, 1.000000 }, /* #dca3a3 */
18 | { 0.764706, 0.749020, 0.623529, 1.000000 }, /* #c3bf9f */
19 | { 0.878431, 0.811765, 0.623529, 1.000000 }, /* #e0cf9f */
20 | { 0.580392, 0.749020, 0.952941, 1.000000 }, /* #94bff3 */
21 | { 0.925490, 0.576471, 0.827451, 1.000000 }, /* #ec93d3 */
22 | { 0.576471, 0.878431, 0.890196, 1.000000 }, /* #93e0e3 */
23 | { 1.000000, 1.000000, 1.000000, 1.000000 }, /* #ffffff */
24 | },
25 | },
26 | #endif /* THEME_ZENBURN */
27 |
--------------------------------------------------------------------------------
/themes/solarized.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_SOLARIZED_H
2 | #define THEME_SOLARIZED_H 1
3 | {
4 | .name = "solarized",
5 | .fg = { 0.611765, 0.760784, 0.764706, 1.000000 }, /* #9cc2c3 */
6 | .bg = { 0.000000, 0.117647, 0.152941, 1.000000 }, /* #001e27 */
7 | .colors = {
8 | { 0.000000, 0.156863, 0.192157, 1.000000 }, /* #002831 */
9 | { 0.819608, 0.109804, 0.141176, 1.000000 }, /* #d11c24 */
10 | { 0.423529, 0.745098, 0.423529, 1.000000 }, /* #6cbe6c */
11 | { 0.647059, 0.466667, 0.023529, 1.000000 }, /* #a57706 */
12 | { 0.129412, 0.462745, 0.780392, 1.000000 }, /* #2176c7 */
13 | { 0.776471, 0.109804, 0.435294, 1.000000 }, /* #c61c6f */
14 | { 0.145098, 0.572549, 0.525490, 1.000000 }, /* #259286 */
15 | { 0.917647, 0.890196, 0.796078, 1.000000 }, /* #eae3cb */
16 | { 0.000000, 0.392157, 0.533333, 1.000000 }, /* #006488 */
17 | { 0.960784, 0.086275, 0.231373, 1.000000 }, /* #f5163b */
18 | { 0.317647, 0.937255, 0.517647, 1.000000 }, /* #51ef84 */
19 | { 0.698039, 0.494118, 0.156863, 1.000000 }, /* #b27e28 */
20 | { 0.090196, 0.556863, 0.784314, 1.000000 }, /* #178ec8 */
21 | { 0.886275, 0.301961, 0.556863, 1.000000 }, /* #e24d8e */
22 | { 0.000000, 0.701961, 0.619608, 1.000000 }, /* #00b39e */
23 | { 0.988235, 0.956863, 0.862745, 1.000000 }, /* #fcf4dc */
24 | },
25 | },
26 | #endif /* THEME_SOLARIZED */
27 |
--------------------------------------------------------------------------------
/themes/jellybeans.h:
--------------------------------------------------------------------------------
1 | #ifndef THEME_JELLYBEANS_H
2 | #define THEME_JELLYBEANS_H 1
3 | {
4 | .name = "jellybeans",
5 | .fg = { 0.870588, 0.870588, 0.870588, 1.000000 }, /* #dedede */
6 | .bg = { 0.070588, 0.070588, 0.070588, 1.000000 }, /* #121212 */
7 | .colors = {
8 | { 0.572549, 0.572549, 0.572549, 1.000000 }, /* #929292 */
9 | { 0.886275, 0.450980, 0.450980, 1.000000 }, /* #e27373 */
10 | { 0.580392, 0.725490, 0.474510, 1.000000 }, /* #94b979 */
11 | { 1.000000, 0.729412, 0.482353, 1.000000 }, /* #ffba7b */
12 | { 0.592157, 0.745098, 0.862745, 1.000000 }, /* #97bedc */
13 | { 0.882353, 0.752941, 0.980392, 1.000000 }, /* #e1c0fa */
14 | { 0.000000, 0.596078, 0.556863, 1.000000 }, /* #00988e */
15 | { 0.870588, 0.870588, 0.870588, 1.000000 }, /* #dedede */
16 | { 0.741176, 0.741176, 0.741176, 1.000000 }, /* #bdbdbd */
17 | { 1.000000, 0.631373, 0.631373, 1.000000 }, /* #ffa1a1 */
18 | { 0.741176, 0.870588, 0.670588, 1.000000 }, /* #bddeab */
19 | { 1.000000, 0.862745, 0.627451, 1.000000 }, /* #ffdca0 */
20 | { 0.694118, 0.847059, 0.964706, 1.000000 }, /* #b1d8f6 */
21 | { 0.984314, 0.854902, 1.000000, 1.000000 }, /* #fbdaff */
22 | { 0.101961, 0.698039, 0.658824, 1.000000 }, /* #1ab2a8 */
23 | { 1.000000, 1.000000, 1.000000, 1.000000 }, /* #ffffff */
24 | },
25 | },
26 | #endif /* THEME_JELLYBEANS */
27 |
--------------------------------------------------------------------------------
/themes/linux.h:
--------------------------------------------------------------------------------
1 | /*
2 | * themes/linux.h
3 | * Copyright (C) 2015 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef THEMES_LINUX_H
9 | #define THEMES_LINUX_H
10 |
11 | /*
12 | * Set of colors as used by GNOME-Terminal for the “Linux” color scheme:
13 | * http://git.gnome.org/browse/gnome-terminal/tree/src/terminal-profile.c
14 | */
15 | {
16 | .name = "linux",
17 | .fg = { 0.8, 0.8, 0.8, 1 },
18 | .bg = { 0, 0, 0, 1 },
19 | .colors = {
20 | { 0, 0, 0, 1 },
21 | { 0.666667, 0, 0, 1 },
22 | { 0, 0.666667, 0, 1 },
23 | { 0.666667, 0.333333, 0, 1 },
24 | { 0, 0, 0.666667, 1 },
25 | { 0.666667, 0, 0.666667, 1 },
26 | { 0, 0.666667, 0.666667, 1 },
27 | { 0.666667, 0.666667, 0.666667, 1 },
28 | { 0.333333, 0.333333, 0.333333, 1 },
29 | { 1, 0.333333, 0.333333, 1 },
30 | { 0.333333, 1, 0.333333, 1 },
31 | { 1, 1, 0.333333, 1 },
32 | { 0.333333, 0.333333, 1, 1 },
33 | { 1, 0.333333, 1, 1 },
34 | { 0.333333, 1, 1, 1 },
35 | { 1, 1, 1, 1 },
36 | }
37 | },
38 |
39 |
40 | #endif /* !THEMES_LINUX_H */
41 |
--------------------------------------------------------------------------------
/menus.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
43 |
44 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ==========================
2 | DWT - Dumb Window Terminal
3 | ==========================
4 |
5 | **DWT is no longer maintained** — you might want to use my
6 | `Termite fork`__ instead.
7 |
8 | .. __: https://github.com/aperezdc/termite
9 |
10 | DWT is a simple, sleek emulator based on the VTE_ widget. Its main features
11 | are being nice-looking (according to my personal taste), not being (very)
12 | configurable, and being a good companion for tiling window managers like
13 | DWM_ (or my own XDWM_ fork). That being said, it works flawlessly and looks
14 | slick in any GTK+-based environment.
15 |
16 | Screenshot
17 | ==========
18 |
19 | .. image:: /screenshots/popover.png
20 |
21 | (Non-)Features
22 | ==============
23 |
24 | * Mostly-fixed configuration:
25 |
26 | - Grey on black color scheme with using Linux console color set.
27 |
28 | - Converts terminal bell beeps in “urgent” X window flags. No visible
29 | or audible terminal bell, besides from that.
30 |
31 | - Non-blinking cursor.
32 |
33 | - Scrollback buffer, but no scrollbar displayed, to save screen real
34 | estate. Use ``Shift-PageUp`` and ``Shift-PageDown`` to scroll.
35 |
36 | - Keybindings to change font size: Use ``Super-+`` and ``Super--``
37 | to change sizes, ``Super-0`` to reset the font.
38 |
39 | - Mouse cursor auto-hide.
40 |
41 | * XTerm-style configurable window title.
42 |
43 | * Clickable URLs. Because on the Internet era being able to quickly open
44 | a browser is a must-have feature.
45 |
46 | * Single process, multiple terminal windows: the first time ``dwt`` is
47 | invoked, it will start a new process; in subsequent times, it will
48 | just create new windows in the existing process.
49 |
50 | * Uses current GTK+ widgets and code conventions. Apart from the popover
51 | and header bar widgets, modern facilities like ``GAction``, property
52 | bindings, and ``GtkApplication`` are used.
53 |
54 |
55 | .. _VTE: http://developer.gnome.org/vte/
56 | .. _DWM: http://dwm.suckless.org/
57 | .. _XDWM: https://github.com/aperezdc/xdwm
58 |
--------------------------------------------------------------------------------
/tools/convert-theme-xrdb:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env lua
2 | --
3 | -- convert-theme-xrdb
4 | -- Copyright (C) 2015 Adrian Perez
5 | --
6 | -- Distributed under terms of the MIT license.
7 | --
8 | if #arg ~= 1 then
9 | io.stderr:write("convert-theme-xrdb: No theme name.\n",
10 | "Usage: convert-theme-xrdb theme-name < input\n")
11 | os.exit(1)
12 | end
13 |
14 |
15 | local line_pattern = "^%#define%s+([%a%_][%w_]*)%s+%#(%x%x)(%x%x)(%x%x)$"
16 | local ansi_nn_color_pattern = "^Ansi_(%d+)_Color$"
17 |
18 | local function prototype(init)
19 | local proto = {}
20 | proto.__index = proto
21 | if type(init) == "function" then
22 | setmetatable(proto, { __call = function (self, ...)
23 | local obj = setmetatable({}, self)
24 | init(obj, ...)
25 | return obj
26 | end })
27 | end
28 | return proto
29 | end
30 |
31 | local Color = prototype(function(self, t)
32 | self.r = t.r or 0.0
33 | self.g = t.g or 0.0
34 | self.b = t.b or 0.0
35 | self.a = t.a or 1.0
36 | end)
37 |
38 | local color_gdk_rgba_template = "{ %.6f, %.6f, %.6f, %.6f }"
39 | function Color:as_gdk_rgba_string()
40 | return color_gdk_rgba_template:format(self.r, self.g, self.b, self.a)
41 | end
42 |
43 | local color_hex_template = "#%02x%02x%02x"
44 | function Color:as_hex_string()
45 | return color_hex_template:format(self.r * 255, self.g * 255, self.b * 255, self.a * 255)
46 | end
47 |
48 |
49 | local color_name_map = {
50 | Background_Color = "bg";
51 | Foreground_Color = "fg";
52 | }
53 | local colors = {}
54 |
55 | for line in io.lines() do
56 | local name, r, g, b = line:match(line_pattern)
57 | if name and r and g and b then
58 | local c = Color {
59 | r = tonumber(r, 16) / 255.0;
60 | g = tonumber(g, 16) / 255.0;
61 | b = tonumber(b, 16) / 255.0;
62 | a = 1.0; -- Always opaque.
63 | }
64 | local key = name:match(ansi_nn_color_pattern)
65 | key = key and (tonumber(key) + 1) or color_name_map[name]
66 | if key then
67 | colors[key] = c
68 | end
69 | end
70 | end
71 |
72 | local header_guard = arg[1]:upper():gsub("[^%a_]", "_")
73 | local theme_name = arg[1]:lower():gsub("[^%a_]", "-")
74 |
75 | io.write("#ifndef THEME_", header_guard, "_H\n")
76 | io.write("#define THEME_", header_guard, "_H 1\n")
77 | io.write("{\n")
78 | io.write(" .name = \"", theme_name, "\",\n")
79 | io.write(" .fg = ", colors.fg:as_gdk_rgba_string(), ", /* ", colors.fg:as_hex_string(), " */\n")
80 | io.write(" .bg = ", colors.bg:as_gdk_rgba_string(), ", /* ", colors.bg:as_hex_string(), " */\n")
81 | io.write(" .colors = {\n")
82 | for _, c in ipairs(colors) do
83 | io.write(" ", c:as_gdk_rgba_string(), ", /* ", c:as_hex_string(), " */\n")
84 | end
85 | io.write(" },\n")
86 | io.write("},\n")
87 | io.write("#endif /* THEME_", header_guard, " */\n")
88 |
--------------------------------------------------------------------------------
/tests/test-settings.c:
--------------------------------------------------------------------------------
1 | /*
2 | * test-settings.c
3 | * Copyright (C) 2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include "../dg-settings.h"
9 | #include "../dg-util.h"
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 |
17 | DG_SETTINGS_CLASS_DECLARE (TestSettings, test_settings)
18 | DG_SETTINGS_CLASS_DEFINE (TestSettings, test_settings)
19 | DG_SETTINGS_BOOLEAN ("foo", "Foo", "Foo", FALSE);
20 | DG_SETTINGS_UINT ("baz", "Baz", "Baz", 12345);
21 | DG_SETTINGS_STRING ("bar", "Bar", "Bar", "BAR");
22 | DG_SETTINGS_CLASS_END
23 |
24 |
25 | static void
26 | delete_settings_dir (gpointer userdata)
27 | {
28 | dg_lmem gchar *dir_path = userdata;
29 | GDir *dir = g_dir_open (dir_path, 0, NULL);
30 | const gchar *name;
31 |
32 | while ((name = g_dir_read_name (dir))) {
33 | dg_lmem gchar *file_path = g_build_filename (dir_path, name, NULL);
34 | g_remove (file_path);
35 | }
36 | g_dir_close (dir);
37 | }
38 |
39 |
40 | static const gchar*
41 | temporary_settings_dir (void)
42 | {
43 | gchar *template = g_build_filename (g_get_tmp_dir (),
44 | "test-settings-XXXXXX",
45 | NULL);
46 | g_test_queue_destroy (delete_settings_dir, template);
47 | return g_mkdtemp (template);
48 | }
49 |
50 |
51 | static void
52 | test_settings_read_defaults (void)
53 | {
54 | TestSettings *settings =
55 | test_settings_new (temporary_settings_dir (), FALSE);
56 | g_test_queue_unref (settings);
57 |
58 | gboolean bool_value;
59 | gchar* string_value = NULL;
60 | guint uint_value = 0;
61 |
62 | g_object_get (G_OBJECT (settings),
63 | "foo", &bool_value,
64 | "bar", &string_value,
65 | "baz", &uint_value,
66 | NULL);
67 | g_test_queue_free (string_value);
68 |
69 | g_assert_false (bool_value);
70 | g_assert_cmpstr (string_value, ==, "BAR");
71 | g_assert_cmpuint (uint_value, ==, 12345);
72 | }
73 |
74 |
75 | static void
76 | populate_setting (const gchar *settings_path,
77 | const gchar *setting_name,
78 | const gchar *value_as_string)
79 | {
80 | dg_lmem gchar *path = g_build_filename (settings_path, setting_name, NULL);
81 | dg_lobj GFile *file = g_file_new_for_path (path);
82 | g_file_replace_contents (file,
83 | value_as_string,
84 | strlen (value_as_string),
85 | NULL,
86 | FALSE,
87 | G_FILE_CREATE_NONE,
88 | NULL,
89 | NULL,
90 | NULL);
91 | }
92 |
93 |
94 | static void
95 | test_settings_read (void)
96 | {
97 | const gchar* settings_path = temporary_settings_dir ();
98 | populate_setting (settings_path, "foo", "TRUE");
99 | populate_setting (settings_path, "bar", "Kitteh sez: meow!");
100 | populate_setting (settings_path, "baz", "42");
101 |
102 | TestSettings *settings = test_settings_new (settings_path, FALSE);
103 | g_test_queue_unref (settings);
104 |
105 | gboolean bool_value;
106 | gchar* string_value = NULL;
107 | guint uint_value = 0;
108 |
109 | g_object_get (G_OBJECT (settings),
110 | "foo", &bool_value,
111 | "bar", &string_value,
112 | "baz", &uint_value,
113 | NULL);
114 | g_test_queue_free (string_value);
115 |
116 | g_assert_true (bool_value);
117 | g_assert_cmpstr (string_value, ==, "Kitteh sez: meow!");
118 | g_assert_cmpuint (uint_value, ==, 42);
119 | }
120 |
121 |
122 | int
123 | main (int argc, char *argv[])
124 | {
125 | g_test_init (&argc, &argv, NULL);
126 | g_test_add_func ("/settings/read-defaults", test_settings_read_defaults);
127 | g_test_add_func ("/settings/read", test_settings_read);
128 | return g_test_run ();
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/dg-settings.h:
--------------------------------------------------------------------------------
1 | /*
2 | * dg-settings.h
3 | * Copyright (C) 2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef DG_SETTINGS_H
9 | #define DG_SETTINGS_H
10 |
11 | #include
12 |
13 | G_BEGIN_DECLS
14 |
15 | #define DG_SETTINGS_TYPE (dg_settings_get_type ())
16 | #define DG_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DG_SETTINGS_TYPE, DgSettings))
17 | #define DG_IS_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DG_SETTINGS_TYPE))
18 |
19 | typedef struct _DgSettingsClass DgSettingsClass;
20 | typedef struct _DgSettings DgSettings;
21 |
22 | struct _DgSettingsClass {
23 | GObjectClass parent_class;
24 | };
25 |
26 | struct _DgSettings {
27 | GObject parent;
28 | };
29 |
30 | GType dg_settings_get_type (void);
31 |
32 | void dg_settings__get_property__ (GObject *object,
33 | guint prop_id,
34 | GValue *value,
35 | GParamSpec *pspec);
36 | void dg_settings__set_property__ (GObject *object,
37 | guint prop_id,
38 | const GValue *value,
39 | GParamSpec *pspec);
40 | void dg_settings__constructed__ (GObject *object);
41 |
42 | #define DG_SETTING__FLAG \
43 | (1 << G_PARAM_USER_SHIFT)
44 | #define DG_SETTING_FLAGS \
45 | (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | DG_SETTING__FLAG)
46 |
47 |
48 | #define DG_SETTINGS_BOOLEAN(_name, _nick, _desc, _default) \
49 | g_object_class_install_property (G_OBJECT_CLASS (klass), \
50 | ++klass->prop_id, \
51 | g_param_spec_boolean ((_name), \
52 | (_nick), \
53 | (_desc), \
54 | (_default), \
55 | DG_SETTING_FLAGS))
56 |
57 | #define DG_SETTINGS_STRING(_name, _nick, _desc, _default) \
58 | g_object_class_install_property (G_OBJECT_CLASS (klass), \
59 | ++klass->prop_id, \
60 | g_param_spec_string ((_name), \
61 | (_nick), \
62 | (_desc), \
63 | (_default), \
64 | DG_SETTING_FLAGS))
65 |
66 | #define DG_SETTINGS_UINT_RANGE(_name, _nick, _desc, _default, _min, _max) \
67 | g_object_class_install_property (G_OBJECT_CLASS (klass), \
68 | ++klass->prop_id, \
69 | g_param_spec_uint ((_name), \
70 | (_nick), \
71 | (_desc), \
72 | (_min), \
73 | (_max), \
74 | (_default), \
75 | DG_SETTING_FLAGS))
76 |
77 | #define DG_SETTINGS_UINT(_name, _nick, _desc, _default) \
78 | DG_SETTINGS_UINT_RANGE ((_name), (_nick), (_desc), (_default), 0, G_MAXUINT)
79 |
80 | #define DG_SETTINGS_CLASS_DECLARE(_T, _t) \
81 | typedef struct _ ## _T ## Class _T ## Class; \
82 | typedef struct _ ## _T _T; \
83 | GType _t ## _get_type (void); \
84 | static inline _T* _t ## _new(const gchar *path, gboolean monitor) { \
85 | return g_object_new (_t ## _get_type(), "settings-path", path, \
86 | "settings-monitoring-enabled", monitor, NULL); }
87 |
88 | #define DG_SETTINGS_CLASS_DEFINE(_T, _t) \
89 | struct _ ## _T ## Class { DgSettingsClass parent_class; guint prop_id; }; \
90 | struct _ ## _T { DgSettings parent; }; \
91 | static void _t ## _init (_T *object) { } \
92 | G_DEFINE_TYPE (_T, _t, DG_SETTINGS_TYPE) \
93 | static void _t ## _class_init (_T ## Class *klass) { \
94 | G_OBJECT_CLASS (klass)->get_property = dg_settings__get_property__; \
95 | G_OBJECT_CLASS (klass)->set_property = dg_settings__set_property__; \
96 | G_OBJECT_CLASS (klass)->constructed = dg_settings__constructed__;
97 | #define DG_SETTINGS_CLASS_END }
98 |
99 | G_END_DECLS
100 |
101 | #endif /* !DG_SETTINGS_H */
102 |
--------------------------------------------------------------------------------
/dwt.rst:
--------------------------------------------------------------------------------
1 | =====
2 | dwt
3 | =====
4 |
5 | ------------------------------
6 | Simple terminal emulator for X
7 | ------------------------------
8 |
9 | :Author: Adrian Perez
10 | :Manual section: 1
11 |
12 |
13 | SYNOPSIS
14 | ========
15 |
16 | ``dwt -e``
17 |
18 |
19 | DESCRIPTION
20 | ===========
21 |
22 | The ``dwt`` program is a terminal emulator for the the X Window System. It
23 | provides DEC VT102/VT220 emulation.
24 |
25 |
26 | USAGE
27 | =====
28 |
29 | Command line options:
30 |
31 | -e COMMAND, --command=COMMAND
32 | Run the given *COMMAND* in the terminal window, instead of the
33 | user shell. If the command has multple arguments, make sure to
34 | quote it properly: the whole command line has to be passed as
35 | a single command line option.
36 |
37 | Setting: ``command`` (*string*).
38 |
39 | -w PATH, --workdir=PATH
40 | Change the active directory to the given *PATH* before running
41 | the shell (or any other command) inside the terminal window.
42 |
43 | -f FONT, --font=FONT
44 | Sets the font used by the terminal emulator. The font name is
45 | interpreted using FontConfig. The default is "terminus 11".
46 |
47 | Setting: ``font`` (*string*).
48 |
49 | -T THEME, --theme=THEME
50 | Choose among one of the built-in themes. If *THEME* is ``list``,
51 | a list of available themes will be printed.
52 |
53 | Setting: ``theme`` (*string*).
54 |
55 | -t TITLE, --title=TITLE
56 | Defines the initial *TITLE* of the terminal window (which, by
57 | default, is "dwt"). Even when a title is specified,
58 | applications may override it using `xterm(1)`-compatible
59 | escape sequences.
60 |
61 | Setting: ``title`` (*string*).
62 |
63 | -U, --no-title-updates
64 | Do not update window titles with those provided by applications
65 | using escape sequences.
66 |
67 | Setting: ``update-title`` (*boolean*).
68 |
69 | -s BYTES, --scrollback=BYTES
70 | Sets the size of the scrollback buffer. The default is 1024
71 | bytes (1 kB).
72 |
73 | Setting: ``scrollback`` (*integer*).
74 |
75 | -b, --bold Allow usage of bold font variants.
76 |
77 | Setting: ``allow-bold`` (*boolean*).
78 |
79 | -N, --no-header-bar
80 | Disable using a custom header bar widget, and let the window
81 | manager use its own decorations instead. This may be useful
82 | to make ``dwt`` blend better with your desktop environment.
83 |
84 | Setting: ``no-header-bar`` (*boolean*).
85 |
86 | -H, --title-on-maximize
87 | Keep a title bar in maximized windows. The default is to
88 | disable the title bar.
89 |
90 | Setting: ``show-title`` (*boolean*).
91 |
92 | -h, --help Show a summary of available options.
93 |
94 |
95 | CONFIGURATION
96 | =============
97 |
98 | ``dwt`` is configured by writing each setting to a configuration file under
99 | ``$XDG_CONFIG_HOME/dwt/`` (typically ``~/.config/dwt/``). In general, only the
100 | first line of each configuration file is read, and each file is used for one
101 | (and only one) setting. This allows to use shell commands to write the
102 | configuration files for ``dwt``. For example::
103 |
104 | echo true > ~/.config/dwt/allow-bold
105 | echo 'Fira Mono 13' > ~/.config/dwt/font
106 |
107 | The following settings are not available as command line options, and are only
108 | settable using configuration files:
109 |
110 | * ``icon`` (*string*): Name of the icon used for application windows.
111 | * ``cursor-color`` (*string*): Cursor color for the terminal widget.
112 | * ``foreground-color`` (*string*): Foreground color of the terminal widget.
113 | If not set, the foreground color from the selected theme is used.
114 | * ``background-color`` (*string*): Background color of the terminal widget.
115 | If not set, the background color from the selected theme is used.
116 | * ``mouse-autohide`` (*boolean*): Automatically hide the mouse pointer on
117 | keypress when it is over a terminal. The mouse pointer will be shown again
118 | when the mouse is moved.
119 | * ``audible-bell`` (*boolean*): Whether the terminal bell is audible.
120 |
121 |
122 | EXAMPLES
123 | ========
124 |
125 | Run Vim in a new terminal window, to edit a file with spaces in its file
126 | name::
127 |
128 | dwt --command='vim "~/foo bar.txt"
129 |
130 | Launch a new terminal window, specifying an alternative title for it::
131 |
132 | dwt --title='Dev Ops'
133 |
134 |
135 | ENVIRONMENT
136 | ===========
137 |
138 | If the ``SHELL`` environment variable is defined, it is assumed that it
139 | contains the path to the shell that will be run as default command in the
140 | terminal window. If not defined, `getpwuid(3)` will be used to determine the
141 | user shell.
142 |
143 | If present, the value of the ``DWT_APPLICATION_ID`` will be used as the
144 | unique identifier for the application. If a ``dwt`` process with the given
145 | identifier is already running, it will be instructed to create a new
146 | terminal window. This means that all the terminal windows created by
147 | launching ``dwt`` with the same identifier live in the same process. To
148 | disable this behaviour, use ``none`` as identifier.
149 |
150 |
151 | SEE ALSO
152 | ========
153 |
154 | `xterm(1)`
155 |
156 |
--------------------------------------------------------------------------------
/dwt-settings.c:
--------------------------------------------------------------------------------
1 | /*
2 | * dwt-settings.c
3 | * Copyright (C) 2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include "dg-settings.h"
9 |
10 | #ifndef DWT_DEFAULT_FONT
11 | #define DWT_DEFAULT_FONT "monospace 11"
12 | #endif /* !DWT_DEFAULT_FONT */
13 |
14 |
15 | DG_SETTINGS_CLASS_DECLARE (DwtSettings, dwt_settings)
16 | DG_SETTINGS_CLASS_DEFINE (DwtSettings, dwt_settings)
17 |
18 | DG_SETTINGS_BOOLEAN ("allow-bold",
19 | "Allow bold fonts",
20 | "Whether to allow usage of bold fonts."
21 | " Note that some type faces may not include"
22 | " a bold variant, and a substitute font which"
23 | " does not match well with the selected font"
24 | " might be chosen, or the base font may be"
25 | " emboldened to create a “fake bold” variant.",
26 | TRUE);
27 |
28 | DG_SETTINGS_BOOLEAN ("mouse-autohide",
29 | "Autohide mouse pointer",
30 | "Whether to automatically hide the mouse cursor"
31 | " on key press when the cursor is over a terminal"
32 | " window. To show the cursor again, move the mouse.",
33 | TRUE);
34 |
35 | DG_SETTINGS_BOOLEAN ("audible-bell",
36 | "Audible terminal bell",
37 | "Whether the terminal bell is audible (i.e. produces"
38 | " sound.",
39 | FALSE);
40 |
41 | DG_SETTINGS_BOOLEAN ("show-title",
42 | "Show window titles",
43 | "Show title bars for maximized terminal windows.",
44 | FALSE);
45 |
46 | DG_SETTINGS_BOOLEAN ("update-title",
47 | "Update window titles",
48 | "Allow updating the titles of windows using the"
49 | " same escape sequence used by XTerm.",
50 | TRUE);
51 |
52 | DG_SETTINGS_BOOLEAN ("no-header-bar",
53 | "Do not use header bars",
54 | "Let the window manager decorate windows instead."
55 | " of using client-side header bars.",
56 | FALSE);
57 |
58 | DG_SETTINGS_UINT_RANGE ("scrollback",
59 | "Scrollback size",
60 | "Number of lines saved as scrollback buffer.",
61 | 0, 0, 10000);
62 |
63 | DG_SETTINGS_STRING ("font",
64 | "Font name",
65 | "Name of the terminal font.",
66 | DWT_DEFAULT_FONT);
67 |
68 | DG_SETTINGS_STRING ("command",
69 | "Command to run",
70 | "Command to run instead of the user shell, if"
71 | " flag '-e' is not specified during invocation.",
72 | NULL);
73 |
74 | DG_SETTINGS_STRING ("title",
75 | "Default window title",
76 | "Initial title to show on terminal windows, if not"
77 | " changed using the XTerm title change escape"
78 | " sequence.",
79 | "dwt");
80 |
81 | DG_SETTINGS_STRING ("icon",
82 | "Terminal window icon",
83 | "Name of the icon used for terminal windows, or"
84 | " path to the file containing the image to be used"
85 | " as icon.",
86 | "terminal");
87 |
88 | DG_SETTINGS_STRING ("cursor-color",
89 | "Cursor color",
90 | "Color of the cursor in active terminal windows,"
91 | " in #RRGGBB hex format.",
92 | "#AA0033");
93 |
94 | DG_SETTINGS_STRING ("background-color",
95 | "Background color",
96 | "Terminal background color. Overrides the color"
97 | " defined in the active theme.",
98 | NULL);
99 |
100 | DG_SETTINGS_STRING ("foreground-color",
101 | "Foreground color",
102 | "Terminal foreground color. Overrides the color"
103 | " defined in the active theme.",
104 | NULL);
105 |
106 | DG_SETTINGS_STRING ("theme",
107 | "Color theme",
108 | "Name of the built-in color theme to be used for the"
109 | " foreground, background, and the 16 basic terminal"
110 | " colors.",
111 | NULL);
112 |
113 | DG_SETTINGS_CLASS_END
114 |
115 |
116 | static gpointer
117 | dwt_settings_create (gpointer dummy)
118 | {
119 | g_autofree char *path = g_build_filename (g_get_user_config_dir (),
120 | g_get_prgname (),
121 | NULL);
122 | return g_object_new (dwt_settings_get_type (),
123 | "settings-path", path,
124 | "settings-monitoring-enabled", TRUE,
125 | NULL);
126 | }
127 |
128 |
129 | DwtSettings*
130 | dwt_settings_get_instance (void)
131 | {
132 | static GOnce instance_once = G_ONCE_INIT;
133 | return g_once (&instance_once, dwt_settings_create, NULL);
134 | }
135 |
--------------------------------------------------------------------------------
/dwt.1:
--------------------------------------------------------------------------------
1 | .\" Man page generated from reStructuredText.
2 | .
3 | .TH DWT 1 "" "" ""
4 | .SH NAME
5 | dwt \- Simple terminal emulator for X
6 | .
7 | .nr rst2man-indent-level 0
8 | .
9 | .de1 rstReportMargin
10 | \\$1 \\n[an-margin]
11 | level \\n[rst2man-indent-level]
12 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
13 | -
14 | \\n[rst2man-indent0]
15 | \\n[rst2man-indent1]
16 | \\n[rst2man-indent2]
17 | ..
18 | .de1 INDENT
19 | .\" .rstReportMargin pre:
20 | . RS \\$1
21 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
22 | . nr rst2man-indent-level +1
23 | .\" .rstReportMargin post:
24 | ..
25 | .de UNINDENT
26 | . RE
27 | .\" indent \\n[an-margin]
28 | .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
29 | .nr rst2man-indent-level -1
30 | .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
31 | .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
32 | ..
33 | .SH SYNOPSIS
34 | .sp
35 | \fBdwt \-e\fP
36 | .SH DESCRIPTION
37 | .sp
38 | The \fBdwt\fP program is a terminal emulator for the the X Window System. It
39 | provides DEC VT102/VT220 emulation.
40 | .SH USAGE
41 | .sp
42 | Command line options:
43 | .INDENT 0.0
44 | .TP
45 | .BI \-e \ COMMAND\fP,\fB \ \-\-command\fB= COMMAND
46 | Run the given \fICOMMAND\fP in the terminal window, instead of the
47 | user shell. If the command has multple arguments, make sure to
48 | quote it properly: the whole command line has to be passed as
49 | a single command line option.
50 | .sp
51 | Setting: \fBcommand\fP (\fIstring\fP).
52 | .TP
53 | .BI \-w \ PATH\fP,\fB \ \-\-workdir\fB= PATH
54 | Change the active directory to the given \fIPATH\fP before running
55 | the shell (or any other command) inside the terminal window.
56 | .TP
57 | .BI \-f \ FONT\fP,\fB \ \-\-font\fB= FONT
58 | Sets the font used by the terminal emulator. The font name is
59 | interpreted using FontConfig. The default is "terminus 11".
60 | .sp
61 | Setting: \fBfont\fP (\fIstring\fP).
62 | .TP
63 | .BI \-T \ THEME\fP,\fB \ \-\-theme\fB= THEME
64 | Choose among one of the built\-in themes. If \fITHEME\fP is \fBlist\fP,
65 | a list of available themes will be printed.
66 | .sp
67 | Setting: \fBtheme\fP (\fIstring\fP).
68 | .TP
69 | .BI \-t \ TITLE\fP,\fB \ \-\-title\fB= TITLE
70 | Defines the initial \fITITLE\fP of the terminal window (which, by
71 | default, is "dwt"). Even when a title is specified,
72 | applications may override it using \fIxterm(1)\fP\-compatible
73 | escape sequences.
74 | .sp
75 | Setting: \fBtitle\fP (\fIstring\fP).
76 | .TP
77 | .B \-U\fP,\fB \-\-no\-title\-updates
78 | Do not update window titles with those provided by applications
79 | using escape sequences.
80 | .sp
81 | Setting: \fBupdate\-title\fP (\fIboolean\fP).
82 | .TP
83 | .BI \-s \ BYTES\fP,\fB \ \-\-scrollback\fB= BYTES
84 | Sets the size of the scrollback buffer. The default is 1024
85 | bytes (1 kB).
86 | .sp
87 | Setting: \fBscrollback\fP (\fIinteger\fP).
88 | .TP
89 | .B \-b\fP,\fB \-\-bold
90 | Allow usage of bold font variants.
91 | .sp
92 | Setting: \fBallow\-bold\fP (\fIboolean\fP).
93 | .TP
94 | .B \-N\fP,\fB \-\-no\-header\-bar
95 | Disable using a custom header bar widget, and let the window
96 | manager use its own decorations instead. This may be useful
97 | to make \fBdwt\fP blend better with your desktop environment.
98 | .sp
99 | Setting: \fBno\-header\-bar\fP (\fIboolean\fP).
100 | .TP
101 | .B \-H\fP,\fB \-\-title\-on\-maximize
102 | Keep a title bar in maximized windows. The default is to
103 | disable the title bar.
104 | .sp
105 | Setting: \fBshow\-title\fP (\fIboolean\fP).
106 | .TP
107 | .B \-h\fP,\fB \-\-help
108 | Show a summary of available options.
109 | .UNINDENT
110 | .SH CONFIGURATION
111 | .sp
112 | \fBdwt\fP is configured by writing each setting to a configuration file under
113 | \fB$XDG_CONFIG_HOME/dwt/\fP (typically \fB~/.config/dwt/\fP). In general, only the
114 | first line of each configuration file is read, and each file is used for one
115 | (and only one) setting. This allows to use shell commands to write the
116 | configuration files for \fBdwt\fP\&. For example:
117 | .INDENT 0.0
118 | .INDENT 3.5
119 | .sp
120 | .nf
121 | .ft C
122 | echo true > ~/.config/dwt/allow\-bold
123 | echo \(aqFira Mono 13\(aq > ~/.config/dwt/font
124 | .ft P
125 | .fi
126 | .UNINDENT
127 | .UNINDENT
128 | .sp
129 | The following settings are not available as command line options, and are only
130 | settable using configuration files:
131 | .INDENT 0.0
132 | .IP \(bu 2
133 | \fBicon\fP (\fIstring\fP): Name of the icon used for application windows.
134 | .IP \(bu 2
135 | \fBcursor\-color\fP (\fIstring\fP): Cursor color for the terminal widget.
136 | .IP \(bu 2
137 | \fBforeground\-color\fP (\fIstring\fP): Foreground color of the terminal widget.
138 | If not set, the foreground color from the selected theme is used.
139 | .IP \(bu 2
140 | \fBbackground\-color\fP (\fIstring\fP): Background color of the terminal widget.
141 | If not set, the background color from the selected theme is used.
142 | .IP \(bu 2
143 | \fBmouse\-autohide\fP (\fIboolean\fP): Automatically hide the mouse pointer on
144 | keypress when it is over a terminal. The mouse pointer will be shown again
145 | when the mouse is moved.
146 | .IP \(bu 2
147 | \fBaudible\-bell\fP (\fIboolean\fP): Whether the terminal bell is audible.
148 | .UNINDENT
149 | .SH EXAMPLES
150 | .sp
151 | Run Vim in a new terminal window, to edit a file with spaces in its file
152 | name:
153 | .INDENT 0.0
154 | .INDENT 3.5
155 | .sp
156 | .nf
157 | .ft C
158 | dwt \-\-command=\(aqvim "~/foo bar.txt"
159 | .ft P
160 | .fi
161 | .UNINDENT
162 | .UNINDENT
163 | .sp
164 | Launch a new terminal window, specifying an alternative title for it:
165 | .INDENT 0.0
166 | .INDENT 3.5
167 | .sp
168 | .nf
169 | .ft C
170 | dwt \-\-title=\(aqDev Ops\(aq
171 | .ft P
172 | .fi
173 | .UNINDENT
174 | .UNINDENT
175 | .SH ENVIRONMENT
176 | .sp
177 | If the \fBSHELL\fP environment variable is defined, it is assumed that it
178 | contains the path to the shell that will be run as default command in the
179 | terminal window. If not defined, \fIgetpwuid(3)\fP will be used to determine the
180 | user shell.
181 | .sp
182 | If present, the value of the \fBDWT_APPLICATION_ID\fP will be used as the
183 | unique identifier for the application. If a \fBdwt\fP process with the given
184 | identifier is already running, it will be instructed to create a new
185 | terminal window. This means that all the terminal windows created by
186 | launching \fBdwt\fP with the same identifier live in the same process. To
187 | disable this behaviour, use \fBnone\fP as identifier.
188 | .SH SEE ALSO
189 | .sp
190 | \fIxterm(1)\fP
191 | .SH AUTHOR
192 | Adrian Perez
193 | .\" Generated by docutils manpage writer.
194 | .
195 |
--------------------------------------------------------------------------------
/dg-settings.c:
--------------------------------------------------------------------------------
1 | /*
2 | * dg-settings.c
3 | * Copyright (C) 2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include "dg-settings.h"
9 | #include
10 |
11 |
12 | enum {
13 | PROP_0,
14 | PROP_SETTINGS_PATH,
15 | PROP_SETTINGS_MONITORING_ENABLED,
16 | PROP_N
17 | };
18 |
19 | typedef struct _DgSettingsPrivate DgSettingsPrivate;
20 |
21 | struct _DgSettingsPrivate {
22 | GFile *settings_path;
23 | gboolean monitor_enabled;
24 | GFileMonitor *monitor;
25 | GValue **values;
26 | };
27 |
28 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (DgSettings, dg_settings, G_TYPE_OBJECT)
29 |
30 |
31 | static GParamSpec *properties[PROP_N] = { NULL, };
32 |
33 |
34 | static gchar*
35 | read_line (GFile *file)
36 | {
37 | g_assert (file != NULL);
38 |
39 | g_autoptr(GError) error = NULL;
40 | g_autoptr(GInputStream) stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
41 |
42 | if (!stream && error)
43 | return NULL;
44 |
45 | g_autoptr(GDataInputStream) datain = g_data_input_stream_new (stream);
46 | gsize read_length = 0;
47 |
48 | return g_data_input_stream_read_line_utf8 (datain,
49 | &read_length,
50 | NULL,
51 | &error);
52 | }
53 |
54 |
55 | static void
56 | read_boolean (GFile *file, GValue *value)
57 | {
58 | g_autofree char *line = read_line (file);
59 | if (line == NULL)
60 | return;
61 |
62 | g_value_set_boolean (value, g_ascii_strcasecmp ("false", line) != 0);
63 | }
64 |
65 |
66 | static void
67 | read_uint (GFile *file, GValue *value)
68 | {
69 | g_autofree char *line = read_line (file);
70 | if (line == NULL)
71 | return;
72 |
73 | guint64 uint_value = g_ascii_strtoull (line, NULL, 0);
74 | if (uint_value <= G_MAXUINT)
75 | g_value_set_uint (value, uint_value);
76 | }
77 |
78 |
79 | void
80 | dg_settings__get_property__ (GObject *object,
81 | guint prop_id,
82 | GValue *value,
83 | GParamSpec *pspec)
84 | {
85 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
86 |
87 | g_autoptr(GFile) setting_file = g_file_get_child (priv->settings_path,
88 | g_param_spec_get_name (pspec));
89 |
90 | /* Use the default value when the file does not exist */
91 | g_value_copy (g_param_spec_get_default_value (pspec), value);
92 | if (!g_file_query_exists (setting_file, NULL))
93 | return;
94 |
95 | /* Try to read the value from the file, converting as appropriate. */
96 | switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
97 | case G_TYPE_BOOLEAN:
98 | read_boolean (setting_file, value);
99 | break;
100 | case G_TYPE_UINT:
101 | read_uint (setting_file, value);
102 | break;
103 | case G_TYPE_STRING:
104 | g_value_take_string (value, read_line (setting_file));
105 | break;
106 | }
107 | }
108 |
109 |
110 | static void
111 | write_line (GFile *file,
112 | GParamSpec *pspec,
113 | const gchar *line)
114 | {
115 | g_assert (file != NULL);
116 | g_assert (pspec != NULL);
117 | g_assert (line != NULL);
118 |
119 | /* TODO: Handle errors */
120 | g_autoptr(GOutputStream) stream =
121 | G_OUTPUT_STREAM (g_file_open_readwrite (file, NULL, NULL));
122 | if (stream == NULL)
123 | return;
124 |
125 | gsize bytes_written;
126 | g_output_stream_printf (stream,
127 | &bytes_written,
128 | NULL,
129 | NULL,
130 | "%s\n\n%s\n",
131 | line,
132 | g_param_spec_get_blurb (pspec));
133 |
134 | g_output_stream_close (stream,
135 | NULL,
136 | NULL);
137 | }
138 |
139 |
140 | void
141 | dg_settings__set_property__ (GObject *object,
142 | guint prop_id,
143 | const GValue *value,
144 | GParamSpec *pspec)
145 | {
146 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
147 |
148 | g_autoptr(GFile) setting_file = g_file_get_child (priv->settings_path,
149 | g_param_spec_get_name (pspec));
150 |
151 | switch (G_PARAM_SPEC_VALUE_TYPE (pspec)) {
152 | case G_TYPE_BOOLEAN:
153 | write_line (setting_file, pspec,
154 | g_value_get_boolean (value) ? "true" : "false");
155 | break;
156 | }
157 | }
158 |
159 |
160 | static void
161 | dg_settings_get_property (GObject *object,
162 | guint prop_id,
163 | GValue *value,
164 | GParamSpec *pspec)
165 | {
166 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
167 |
168 | switch (prop_id) {
169 | case PROP_SETTINGS_PATH: {
170 | g_value_take_string (value, g_file_get_path (priv->settings_path));
171 | break;
172 | }
173 | case PROP_SETTINGS_MONITORING_ENABLED:
174 | g_value_set_boolean (value, priv->monitor_enabled);
175 | break;
176 | default:
177 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178 | }
179 | }
180 |
181 |
182 | static void
183 | dg_settings_set_property (GObject *object,
184 | guint prop_id,
185 | const GValue *value,
186 | GParamSpec *pspec)
187 | {
188 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
189 |
190 | switch (prop_id) {
191 | case PROP_SETTINGS_PATH:
192 | g_assert (!priv->settings_path);
193 | priv->settings_path = g_file_new_for_path (g_value_get_string (value));
194 | break;
195 | case PROP_SETTINGS_MONITORING_ENABLED:
196 | priv->monitor_enabled = g_value_get_boolean (value);
197 | break;
198 | default:
199 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200 | }
201 | }
202 |
203 |
204 | static void
205 | dg_settings_finalize (GObject *object)
206 | {
207 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
208 | if (priv->monitor_enabled)
209 | g_object_unref (priv->monitor);
210 | g_object_unref (priv->settings_path);
211 | G_OBJECT_CLASS (dg_settings_parent_class)->finalize (object);
212 | }
213 |
214 |
215 | static void
216 | dg_settings_init (DgSettings *settings)
217 | {
218 | }
219 |
220 |
221 | static void
222 | dg_settings_monitor_changed (GFileMonitor *monitor,
223 | GFile *file,
224 | GFile *other_file,
225 | GFileMonitorEvent event,
226 | DgSettings *settings)
227 | {
228 | switch (event) {
229 | case G_FILE_MONITOR_EVENT_CHANGED:
230 | case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
231 | case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
232 | case G_FILE_MONITOR_EVENT_UNMOUNTED:
233 | case G_FILE_MONITOR_EVENT_MOVED:
234 | case G_FILE_MONITOR_EVENT_RENAMED:
235 | case G_FILE_MONITOR_EVENT_MOVED_IN:
236 | case G_FILE_MONITOR_EVENT_MOVED_OUT:
237 | case G_FILE_MONITOR_EVENT_CREATED:
238 | return;
239 |
240 | case G_FILE_MONITOR_EVENT_DELETED:
241 | case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: {
242 | g_autofree char *filename = g_file_get_basename (file);
243 | GParamSpec *pspec =
244 | g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
245 | filename);
246 | if (pspec) g_object_notify_by_pspec (G_OBJECT (settings), pspec);
247 | }
248 | }
249 | }
250 |
251 |
252 | void
253 | dg_settings__constructed__ (GObject *object)
254 | {
255 | DgSettingsPrivate *priv = dg_settings_get_instance_private (DG_SETTINGS (object));
256 |
257 | g_assert (priv->settings_path);
258 | if (priv->monitor_enabled) {
259 | g_autoptr(GError) error = NULL;
260 | priv->monitor = g_file_monitor_directory (priv->settings_path,
261 | G_FILE_MONITOR_NONE,
262 | NULL,
263 | &error);
264 | if (priv->monitor) {
265 | g_file_monitor_set_rate_limit (priv->monitor, 500);
266 | g_signal_connect (priv->monitor,
267 | "changed",
268 | G_CALLBACK (dg_settings_monitor_changed),
269 | object);
270 | } else {
271 | g_autofree char *path = g_file_get_path (priv->settings_path);
272 | g_warning ("DgSettings: cannot initialize monitoring for directory '%s'\n", path);
273 | if (error) g_warning ("DgSettings: %s\n", error->message);
274 |
275 | g_warning ("DgSettings: trying to continue anyway with monitoring disabled\n");
276 | priv->monitor_enabled = FALSE;
277 | }
278 | }
279 | }
280 |
281 |
282 | static void
283 | dg_settings_class_init (DgSettingsClass *klass)
284 | {
285 | GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
286 | g_object_class->get_property = dg_settings_get_property;
287 | g_object_class->set_property = dg_settings_set_property;
288 | g_object_class->finalize = dg_settings_finalize;
289 |
290 | properties[PROP_SETTINGS_PATH] =
291 | g_param_spec_string ("settings-path",
292 | "Settings path",
293 | "Directory where the configuration files reside",
294 | NULL,
295 | G_PARAM_READWRITE |
296 | G_PARAM_CONSTRUCT_ONLY |
297 | G_PARAM_STATIC_STRINGS);
298 |
299 | properties[PROP_SETTINGS_MONITORING_ENABLED] =
300 | g_param_spec_boolean ("settings-monitoring-enabled",
301 | "Settings monitoring enabled",
302 | "Whether changes to files backing up settings will be monitored",
303 | FALSE,
304 | G_PARAM_READWRITE |
305 | G_PARAM_CONSTRUCT_ONLY |
306 | G_PARAM_STATIC_STRINGS);
307 |
308 | g_object_class_install_properties (g_object_class, PROP_N, properties);
309 | }
310 |
311 |
312 |
313 |
--------------------------------------------------------------------------------
/dwt.c:
--------------------------------------------------------------------------------
1 | /*
2 | * dwt.c
3 | * Copyright (C) 2012-2014 Adrian Perez
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #define PCRE2_CODE_UNIT_WIDTH 8
9 |
10 | #define DWT_GRESOURCE(name) ("/org/perezdecastro/dwt/" name)
11 |
12 | #include "dwt-settings.h"
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #ifdef GDK_WINDOWING_X11
23 | # include
24 | #endif
25 |
26 | #define CHECK_FLAGS(_v, _f) (((_v) & (_f)) == (_f))
27 | #define NDIGITS10(_t) (sizeof (_t) * __CHAR_BIT__ * 3 / 10)
28 |
29 | /* Last matched text piece. */
30 | static gchar *last_match_text = NULL;
31 |
32 | /* Default font size */
33 | static gint default_font_size = 0;
34 |
35 |
36 | /* Forward declarations. */
37 | static GtkWidget*
38 | create_new_window (GtkApplication *application,
39 | GVariantDict *options);
40 |
41 |
42 | static const GOptionEntry option_entries[] =
43 | {
44 | {
45 | "command", 'e',
46 | G_OPTION_FLAG_IN_MAIN,
47 | G_OPTION_ARG_STRING,
48 | NULL,
49 | "Execute the argument to this option inside the terminal",
50 | "COMMAND",
51 | }, {
52 | "workdir", 'w',
53 | G_OPTION_FLAG_IN_MAIN,
54 | G_OPTION_ARG_STRING,
55 | NULL,
56 | "Set working directory before running the command/shell",
57 | "PATH",
58 | }, {
59 | "font", 'f',
60 | G_OPTION_FLAG_IN_MAIN,
61 | G_OPTION_ARG_STRING,
62 | NULL,
63 | "Font used by the terminal, in FontConfig syntax",
64 | "FONT",
65 | }, {
66 | "theme", 'T',
67 | G_OPTION_FLAG_IN_MAIN,
68 | G_OPTION_ARG_STRING,
69 | NULL,
70 | "Choose a built-in color theme",
71 | "NAME",
72 | }, {
73 | "title", 't',
74 | G_OPTION_FLAG_IN_MAIN,
75 | G_OPTION_ARG_STRING,
76 | NULL,
77 | "Initial terminal window title",
78 | "TITLE",
79 | }, {
80 | "no-title-updates", 'U',
81 | G_OPTION_FLAG_IN_MAIN,
82 | G_OPTION_ARG_NONE,
83 | NULL,
84 | "Do not update window title automatically",
85 | NULL,
86 | }, {
87 | "scrollback", 's',
88 | G_OPTION_FLAG_IN_MAIN,
89 | G_OPTION_ARG_INT,
90 | NULL,
91 | "Scrollback buffer size, in bytes (default 1024)",
92 | "BYTES"
93 | }, {
94 | "bold", 'b',
95 | G_OPTION_FLAG_IN_MAIN,
96 | G_OPTION_ARG_NONE,
97 | NULL,
98 | "Allow using bold fonts",
99 | NULL,
100 | }, {
101 | "title-on-maximize", 'H',
102 | G_OPTION_FLAG_IN_MAIN,
103 | G_OPTION_ARG_NONE,
104 | NULL,
105 | "Always show title bar when window is maximized.",
106 | NULL,
107 | }, {
108 | "no-header-bar", 'N',
109 | G_OPTION_FLAG_IN_MAIN,
110 | G_OPTION_ARG_NONE,
111 | NULL,
112 | "Disable header bars in terminal windows (use window manager decorations)",
113 | NULL,
114 | },
115 | { NULL }
116 | };
117 |
118 |
119 | typedef struct {
120 | const gchar *name;
121 | GdkRGBA fg, bg;
122 | GdkRGBA colors[16];
123 | } Theme;
124 |
125 |
126 | /* The first element is the "default" theme. */
127 | static const Theme themes[] = {
128 | #include "themes/linux.h"
129 | #include "themes/jellybeans.h"
130 | #include "themes/monokai.h"
131 | #include "themes/molokai.h"
132 | #include "themes/solarized.h"
133 | #include "themes/thayer.h"
134 | #include "themes/wombat.h"
135 | #include "themes/zenburn.h"
136 | };
137 |
138 |
139 | static GdkRGBA cursor_active = { 0, 0.75, 0, 1 };
140 | static GdkRGBA cursor_inactive = { 0.6, 0.6, 0.6, 1 };
141 |
142 |
143 | /* Regexp used to match URIs and allow clicking them */
144 | static const gchar uri_regexp[] = "(ftp|http)s?://[-@a-zA-Z0-9.?$%&/=_~#.,:;+]*";
145 | static const gchar image_regex_string[] = "/[^/]+\\.(png|jpg|jpeg|gif|webp)$";
146 |
147 | static GRegex *image_regex = NULL;
148 |
149 |
150 | static const Theme* const
151 | find_theme (const gchar *name)
152 | {
153 | for (size_t i = 0; i < G_N_ELEMENTS (themes); i++) {
154 | if (g_str_equal (name, themes[i].name)) {
155 | return &themes[i];
156 | }
157 | }
158 | return NULL;
159 | }
160 |
161 |
162 | #define SWAP(t, a, b) \
163 | do { \
164 | t tmp_ ## __LINE__ = (a); \
165 | (a) = (b); \
166 | (b) = tmp_ ## __LINE__; \
167 | } while (0)
168 |
169 | static void
170 | configure_term_widget (VteTerminal *vtterm,
171 | GVariantDict *options)
172 | {
173 | /* Pick default settings from the settings... */
174 | g_autofree char *opt_font = NULL;
175 | g_autofree char *opt_theme = NULL;
176 | g_autofree char *opt_fgcolor = NULL;
177 | g_autofree char *opt_bgcolor = NULL;
178 | gboolean opt_bold;
179 | guint opt_scroll;
180 | DwtSettings *settings = dwt_settings_get_instance ();
181 |
182 | g_object_get (settings,
183 | "font", &opt_font,
184 | "theme", &opt_theme,
185 | "allow-bold", &opt_bold,
186 | "scrollback", &opt_scroll,
187 | "foreground-color", &opt_fgcolor,
188 | "background-color", &opt_bgcolor,
189 | NULL);
190 |
191 | /*
192 | * This ensures that properties are updated for the terminal whenever they
193 | * change in the configuration files.
194 | *
195 | * TODO: For now this is done only for those properties which cannot be
196 | * overriden using command line flags.
197 | */
198 | static const struct {
199 | const gchar *setting_name;
200 | const gchar *property_name;
201 | GBindingFlags bind_flags;
202 | } property_bind_map[] = {
203 | { "mouse-autohide", "pointer-autohide", G_BINDING_SYNC_CREATE },
204 | { "audible-bell", "audible-bell", G_BINDING_SYNC_CREATE },
205 | };
206 | for (guint i = 0; i < G_N_ELEMENTS (property_bind_map); i++) {
207 | g_object_bind_property (settings,
208 | property_bind_map[i].setting_name,
209 | G_OBJECT (vtterm),
210 | property_bind_map[i].property_name,
211 | property_bind_map[i].bind_flags);
212 | }
213 |
214 | /* ...and allow command line options to override them. */
215 | if (options) {
216 | g_autofree char *cmd_font = NULL;
217 | g_variant_dict_lookup (options, "font", "s", &cmd_font);
218 | if (cmd_font) SWAP (gchar*, cmd_font, opt_font);
219 |
220 | g_autofree char *cmd_theme = NULL;
221 | g_variant_dict_lookup (options, "theme", "s", &cmd_theme);
222 | if (cmd_theme) SWAP (gchar*, cmd_theme, opt_theme);
223 |
224 | g_variant_dict_lookup (options, "allow-bold", "b", &opt_bold);
225 | g_variant_dict_lookup (options, "scrollback", "u", &opt_scroll);
226 | }
227 |
228 | PangoFontDescription *fontd = pango_font_description_from_string (opt_font);
229 | if (fontd) {
230 | if (!pango_font_description_get_family (fontd))
231 | pango_font_description_set_family_static (fontd, "monospace");
232 | if (!pango_font_description_get_size (fontd))
233 | pango_font_description_set_size (fontd, 12 * PANGO_SCALE);
234 | vte_terminal_set_font (vtterm, fontd);
235 | pango_font_description_free (fontd);
236 | fontd = NULL;
237 | }
238 |
239 | const Theme *theme = &themes[1];
240 | if (opt_theme) {
241 | theme = find_theme (opt_theme);
242 | if (!theme) {
243 | g_printerr ("No such theme '%s', using default (linux)\n", opt_theme);
244 | theme = &themes[1];
245 | }
246 | }
247 |
248 | GdkRGBA fgcolor, bgcolor;
249 | if (!(opt_fgcolor && gdk_rgba_parse (&fgcolor, opt_fgcolor)))
250 | fgcolor = theme->fg;
251 | if (!(opt_bgcolor && gdk_rgba_parse (&bgcolor, opt_bgcolor)))
252 | bgcolor = theme->bg;
253 |
254 | vte_terminal_set_rewrap_on_resize (vtterm, TRUE);
255 | vte_terminal_set_scroll_on_keystroke (vtterm, TRUE);
256 | vte_terminal_set_audible_bell (vtterm, FALSE);
257 | vte_terminal_set_scroll_on_output (vtterm, FALSE);
258 | vte_terminal_set_allow_bold (vtterm, opt_bold);
259 | vte_terminal_set_scrollback_lines (vtterm, opt_scroll);
260 | vte_terminal_set_cursor_blink_mode (vtterm, VTE_CURSOR_BLINK_OFF);
261 | vte_terminal_set_cursor_shape (vtterm, VTE_CURSOR_SHAPE_BLOCK);
262 | vte_terminal_set_colors (vtterm,
263 | &fgcolor,
264 | &bgcolor,
265 | theme->colors,
266 | G_N_ELEMENTS (theme->colors));
267 |
268 | g_autoptr(GError) error = NULL;
269 | g_autoptr(VteRegex) regex =
270 | vte_regex_new_for_match (uri_regexp, -1,
271 | PCRE2_CASELESS | PCRE2_MULTILINE,
272 | &error);
273 | if (regex) {
274 | if (!vte_regex_jit (regex, PCRE2_JIT_COMPLETE, &error))
275 | g_warning ("Could not JIT-compile URI regex: %s", error->message);
276 | int tag = vte_terminal_match_add_regex (vtterm, regex, PCRE2_NOTEMPTY);
277 | vte_terminal_match_set_cursor_type (vtterm, tag, GDK_HAND2);
278 | } else {
279 | g_critical ("Could not compile URI regex: %s", error->message);
280 | }
281 | }
282 |
283 |
284 | static void
285 | term_beeped (VteTerminal *vtterm, gpointer userdata)
286 | {
287 | /*
288 | * Only set the _URGENT hint when the window is not focused. If the
289 | * window is focused, the user is likely to notice what is happening
290 | * without the need to call for attention.
291 | */
292 | if (!gtk_window_has_toplevel_focus (GTK_WINDOW (userdata)))
293 | gtk_window_set_urgency_hint (GTK_WINDOW (userdata), TRUE);
294 | }
295 |
296 |
297 | static void
298 | term_char_size_changed (VteTerminal *vtterm,
299 | guint width,
300 | guint height,
301 | gpointer userdata)
302 | {
303 | GdkGeometry geometry;
304 | geometry.height_inc = height;
305 | geometry.width_inc = width;
306 |
307 | GtkBorder padding;
308 | gtk_style_context_get_padding (gtk_widget_get_style_context (GTK_WIDGET (vtterm)),
309 | gtk_widget_get_state_flags (GTK_WIDGET (vtterm)),
310 | &padding);
311 | geometry.base_height = padding.top + padding.bottom;
312 | geometry.base_width = padding.left + padding.right;
313 |
314 | geometry.min_height = geometry.base_height + 3 * geometry.height_inc;
315 | geometry.min_width = geometry.base_width + 10 * geometry.width_inc;
316 |
317 | gtk_window_set_geometry_hints (GTK_WINDOW (userdata),
318 | GTK_WIDGET (vtterm),
319 | &geometry,
320 | GDK_HINT_MIN_SIZE |
321 | GDK_HINT_BASE_SIZE |
322 | GDK_HINT_RESIZE_INC);
323 | gtk_widget_queue_resize (GTK_WIDGET (vtterm));
324 | }
325 |
326 |
327 | static char*
328 | guess_shell (void)
329 | {
330 | char *shell = getenv ("SHELL");
331 | if (!shell) {
332 | struct passwd *pw = getpwuid (getuid ());
333 | shell = (pw) ? pw->pw_shell : "/bin/sh";
334 | }
335 | /* Return a copy */
336 | return g_strdup (shell);
337 | }
338 |
339 |
340 | static gboolean
341 | popover_idle_closed_tick (gpointer userdata)
342 | {
343 | gtk_widget_grab_focus (GTK_WIDGET (userdata));
344 | return FALSE; /* Do no re-arm (run once) */
345 | }
346 |
347 | static void
348 | popover_closed (GtkPopover *popover,
349 | VteTerminal *vtterm)
350 | {
351 | /* XXX: Grabbing the focus right away does not work, must do later. */
352 | g_idle_add (popover_idle_closed_tick, vtterm);
353 | }
354 |
355 | static GtkWidget*
356 | setup_popover (VteTerminal *vtterm)
357 | {
358 | GtkWidget *popover = gtk_popover_new (GTK_WIDGET (vtterm));
359 | g_signal_connect (G_OBJECT (popover), "closed",
360 | G_CALLBACK (popover_closed), vtterm);
361 |
362 | g_autoptr(GtkBuilder) builder = gtk_builder_new_from_resource (DWT_GRESOURCE ("menus.xml"));
363 | gtk_popover_bind_model (GTK_POPOVER (popover),
364 | G_MENU_MODEL (gtk_builder_get_object (builder, "popover-menu")),
365 | NULL);
366 | return popover;
367 | }
368 |
369 |
370 | static void
371 | image_popover_closed (GtkWidget *popover,
372 | gpointer userdata)
373 | {
374 | gtk_widget_destroy (popover);
375 | }
376 |
377 |
378 | static void
379 | image_pixbuf_loaded (GInputStream *stream,
380 | GAsyncResult *result,
381 | GtkWidget *popover)
382 | {
383 | g_autoptr(GError) error = NULL;
384 | g_autoptr(GdkPixbuf) pixbuf = gdk_pixbuf_new_from_stream_finish (result,
385 | &error);
386 | if (!pixbuf || error) {
387 | g_printerr ("Could not decode image from pixbuf: %s", error->message);
388 | return;
389 | }
390 |
391 | gtk_container_add (GTK_CONTAINER (popover),
392 | gtk_image_new_from_pixbuf (pixbuf));
393 | g_signal_connect (popover, "closed",
394 | G_CALLBACK (image_popover_closed), NULL);
395 | gtk_widget_show_all (popover);
396 | }
397 |
398 |
399 | static void
400 | image_file_opened (GFile *file,
401 | GAsyncResult *result,
402 | GtkWidget *popover)
403 | {
404 | g_autoptr(GError) error = NULL;
405 | g_autoptr(GFileInputStream) stream = g_file_read_finish (file, result, &error);
406 | if (!stream || error) {
407 | g_autofree char *uri = g_file_get_uri (file);
408 | g_printerr ("Could not open URL '%s': %s", uri, error->message);
409 | return;
410 | }
411 |
412 | gdk_pixbuf_new_from_stream_at_scale_async (G_INPUT_STREAM (stream),
413 | 500, 500, TRUE,
414 | NULL,
415 | (GAsyncReadyCallback) image_pixbuf_loaded,
416 | popover);
417 | }
418 |
419 |
420 | static GtkWidget*
421 | make_popover_for_image_url (VteTerminal *vtterm,
422 | const gchar *uri)
423 | {
424 | g_assert (vtterm);
425 | g_assert (uri);
426 |
427 | GtkWidget *popover = gtk_popover_new (GTK_WIDGET (vtterm));
428 | g_autoptr(GVfs) gvfs = g_vfs_get_default ();
429 | g_autoptr(GFile) file = g_vfs_get_file_for_uri (gvfs, uri);
430 | g_file_read_async (file,
431 | G_PRIORITY_DEFAULT,
432 | NULL,
433 | (GAsyncReadyCallback) image_file_opened,
434 | popover);
435 | return popover;
436 | }
437 |
438 |
439 | static gboolean
440 | term_mouse_button_released (VteTerminal *vtterm,
441 | GdkEventButton *event,
442 | gpointer userdata)
443 | {
444 | g_clear_pointer (&last_match_text, g_free);
445 |
446 | int match_tag;
447 | g_autofree char *match =
448 | vte_terminal_match_check_event (vtterm, (GdkEvent*) event, &match_tag);
449 |
450 | const long col = event->x / vte_terminal_get_char_width (vtterm);
451 | const long row = event->y / vte_terminal_get_char_height (vtterm);
452 |
453 | GtkWindow *window = GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (vtterm),
454 | GTK_TYPE_WINDOW));
455 |
456 | if (match && event->button == 1) {
457 | if (CHECK_FLAGS (event->state, GDK_CONTROL_MASK)) {
458 | g_autoptr(GError) error = NULL;
459 | if (!gtk_show_uri_on_window (window, match, event->time, &error))
460 | g_printerr ("Could not open URL: %s\n", error->message);
461 | return FALSE;
462 | } else if (g_regex_match (image_regex, match, 0, NULL)) {
463 | /* Show picture in a popover */
464 | GdkRectangle rect;
465 | rect.height = vte_terminal_get_char_height (vtterm);
466 | rect.width = vte_terminal_get_char_width (vtterm);
467 | rect.y = rect.height * row;
468 | rect.x = rect.width * col;
469 |
470 | GtkWidget* popover = make_popover_for_image_url (vtterm, match);
471 | gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rect);
472 | return FALSE;
473 | }
474 | }
475 |
476 |
477 | if (event->button == 3 && userdata != NULL) {
478 | GdkRectangle rect;
479 | rect.height = vte_terminal_get_char_height (vtterm);
480 | rect.width = vte_terminal_get_char_width (vtterm);
481 | rect.y = rect.height * row;
482 | rect.x = rect.width * col;
483 | gtk_popover_set_pointing_to (GTK_POPOVER (userdata), &rect);
484 |
485 | GActionMap *actions = G_ACTION_MAP (window);
486 | g_simple_action_set_enabled (G_SIMPLE_ACTION (g_action_map_lookup_action (actions,
487 | "copy")),
488 | vte_terminal_get_has_selection (vtterm));
489 | g_simple_action_set_enabled (G_SIMPLE_ACTION (g_action_map_lookup_action (actions,
490 | "open-url")),
491 | match != NULL);
492 | g_simple_action_set_enabled (G_SIMPLE_ACTION (g_action_map_lookup_action (actions,
493 | "copy-url")),
494 | match != NULL);
495 | if (match) {
496 | last_match_text = match;
497 | match = NULL;
498 | }
499 |
500 | gtk_widget_show_all (GTK_WIDGET (userdata));
501 |
502 | return TRUE;
503 | }
504 |
505 | return FALSE;
506 | }
507 |
508 | static gboolean
509 | beeped_revealer_timeout (gpointer userdata)
510 | {
511 | GtkWindow *window =
512 | GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (userdata),
513 | GTK_TYPE_WINDOW));
514 | if (!gtk_window_has_toplevel_focus (window))
515 | return TRUE;
516 |
517 | gtk_revealer_set_reveal_child (GTK_REVEALER (userdata), FALSE);
518 | return FALSE; /* Do not re-arm timer, run only once */
519 | }
520 |
521 | static void
522 | header_bar_term_beeped (VteTerminal *vtterm,
523 | GtkRevealer *revealer)
524 | {
525 | /* If already shown, do nothing */
526 | if (gtk_revealer_get_reveal_child (revealer))
527 | return;
528 |
529 | gtk_revealer_set_reveal_child (revealer, TRUE);
530 | g_timeout_add_seconds (3, beeped_revealer_timeout, revealer);
531 | }
532 |
533 | static void
534 | setup_header_bar (GtkWidget *window,
535 | VteTerminal *vtterm,
536 | gboolean show_maximized_title)
537 | {
538 | /*
539 | * Using the default GtkHeaderBar title/subtitle widget makes the bar
540 | * too thick to look nice for a terminal, so set a custom widget.
541 | */
542 | const gchar *title = gtk_window_get_title (GTK_WINDOW (window));
543 | GtkWidget *label = gtk_label_new (title ? title : "dwt");
544 | g_object_bind_property (G_OBJECT (vtterm), "window-title",
545 | G_OBJECT (label), "label",
546 | G_BINDING_DEFAULT);
547 |
548 | GtkWidget *header = gtk_header_bar_new ();
549 | gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), TRUE);
550 | gtk_header_bar_set_has_subtitle (GTK_HEADER_BAR (header), FALSE);
551 | gtk_header_bar_set_custom_title (GTK_HEADER_BAR (header), label);
552 |
553 | GtkWidget *button = gtk_button_new_from_icon_name ("tab-new-symbolic",
554 | GTK_ICON_SIZE_BUTTON);
555 | gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
556 | gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "app.new-terminal");
557 | gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
558 |
559 | GtkWidget *revealer = gtk_revealer_new ();
560 | gtk_container_add (GTK_CONTAINER (revealer),
561 | gtk_image_new_from_icon_name ("software-update-urgent-symbolic",
562 | GTK_ICON_SIZE_BUTTON));
563 | gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 500);
564 | gtk_revealer_set_transition_type (GTK_REVEALER (revealer),
565 | GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
566 | gtk_header_bar_pack_end (GTK_HEADER_BAR (header), revealer);
567 |
568 | g_signal_connect (G_OBJECT (vtterm), "bell",
569 | G_CALLBACK (header_bar_term_beeped), revealer);
570 | g_object_bind_property (G_OBJECT (window), "urgency-hint",
571 | G_OBJECT (revealer), "reveal-child",
572 | G_BINDING_DEFAULT);
573 |
574 | gtk_window_set_titlebar (GTK_WINDOW (window), header);
575 |
576 | /* Hide the header bar when the window is maximized. */
577 | if (!show_maximized_title) {
578 | g_object_bind_property (G_OBJECT (window), "is-maximized",
579 | G_OBJECT (header), "visible",
580 | G_BINDING_INVERT_BOOLEAN);
581 | }
582 | }
583 |
584 |
585 | static void
586 | window_has_toplevel_focus_notified (GObject *object,
587 | GParamSpec *pspec,
588 | gpointer userdata)
589 | {
590 | if (gtk_window_has_toplevel_focus (GTK_WINDOW (object))) {
591 | vte_terminal_set_color_cursor (VTE_TERMINAL (userdata),
592 | &cursor_active);
593 | /* Clear the _URGENT hint when the window gets activated. */
594 | gtk_window_set_urgency_hint (GTK_WINDOW (object), FALSE);
595 | } else {
596 | vte_terminal_set_color_cursor (VTE_TERMINAL (userdata),
597 | &cursor_inactive);
598 | }
599 | }
600 |
601 |
602 | static void
603 | term_child_exited (VteTerminal *vtterm,
604 | gint status,
605 | gpointer userdata)
606 | {
607 | /*
608 | * Destroy the window when the terminal child is exited. Note that this
609 | * will fire the "delete-event" signal, and its handler already takes
610 | * care of deregistering the window in the GtkApplication.
611 | */
612 | gtk_window_close (GTK_WINDOW (userdata));
613 | }
614 |
615 |
616 | static VteTerminal*
617 | window_get_term_widget (GtkWindow *window)
618 | {
619 | GtkWidget *widget = gtk_bin_get_child (GTK_BIN (window));
620 | if (GTK_IS_BIN (widget))
621 | widget = gtk_bin_get_child (GTK_BIN (widget));
622 | return VTE_TERMINAL (widget);
623 | }
624 |
625 |
626 | static void
627 | font_size_action_ativated (GSimpleAction *action,
628 | GVariant *parameter,
629 | gpointer userdata)
630 | {
631 | VteTerminal *vtterm = window_get_term_widget (GTK_WINDOW (userdata));
632 | const gint modifier = g_variant_get_int32 (parameter);
633 |
634 | const PangoFontDescription *fontd = vte_terminal_get_font (vtterm);
635 | gint old_size = pango_font_description_get_size (fontd);
636 | if (default_font_size == 0)
637 | default_font_size = old_size;
638 | PangoFontDescription *new_fontd;
639 | gint new_size;
640 |
641 | switch (modifier) {
642 | case 0:
643 | old_size = default_font_size;
644 | /* fall-through */
645 | case 1:
646 | case -1:
647 | new_size = old_size + modifier * PANGO_SCALE;
648 | new_fontd = pango_font_description_copy_static (fontd);
649 | pango_font_description_set_size (new_fontd, new_size);
650 | vte_terminal_set_font (vtterm, new_fontd);
651 | break;
652 |
653 | default:
654 | g_printerr ("%s: invalid modifier '%i'", __func__, modifier);
655 | return;
656 | }
657 |
658 | pango_font_description_free (new_fontd);
659 | }
660 |
661 |
662 | static void
663 | paste_action_activated (GSimpleAction *action,
664 | GVariant *parameter,
665 | gpointer userdata)
666 | {
667 | vte_terminal_paste_clipboard (window_get_term_widget (GTK_WINDOW (userdata)));
668 | }
669 |
670 |
671 | static void
672 | copy_action_activated (GSimpleAction *action,
673 | GVariant *parameter,
674 | gpointer userdata)
675 | {
676 | VteTerminal *vtterm = window_get_term_widget (GTK_WINDOW (userdata));
677 | vte_terminal_copy_clipboard_format (vtterm, VTE_FORMAT_TEXT);
678 | vte_terminal_copy_primary (vtterm);
679 | }
680 |
681 |
682 | static void
683 | new_terminal_action_activated (GSimpleAction *action,
684 | GVariant *parameter,
685 | gpointer userdata)
686 | {
687 | create_new_window (GTK_APPLICATION (userdata), NULL);
688 | }
689 |
690 |
691 | static void
692 | about_action_activated (GSimpleAction *action,
693 | GVariant *parameter,
694 | gpointer userdata)
695 | {
696 | static const gchar* authors[] = {
697 | "Adrián Pérez de Castro",
698 | NULL,
699 | };
700 |
701 | gtk_show_about_dialog (NULL,
702 | "authors", authors,
703 | "logo-icon-name", "terminal",
704 | "license-type", GTK_LICENSE_MIT_X11,
705 | "comments", "A sleek terminal emulator",
706 | "website", "https://github.com/aperezdc/dwt",
707 | NULL);
708 | }
709 |
710 |
711 | static void
712 | quit_action_activated (GSimpleAction *action,
713 | GVariant *parameter,
714 | gpointer userdata)
715 | {
716 | g_application_quit (G_APPLICATION (userdata));
717 | }
718 |
719 |
720 | static void
721 | copy_url_action_activated (GSimpleAction *action,
722 | GVariant *parameter,
723 | gpointer userdata)
724 | {
725 | gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
726 | last_match_text, -1);
727 | gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
728 | last_match_text, -1);
729 |
730 | g_free (last_match_text);
731 | last_match_text = NULL;
732 | }
733 |
734 |
735 | static void
736 | open_url_action_activated (GSimpleAction *action,
737 | GVariant *parameter,
738 | gpointer userdata)
739 | {
740 | g_autoptr(GError) error = NULL;
741 | if (!gtk_show_uri_on_window (GTK_WINDOW (userdata),
742 | last_match_text,
743 | GDK_CURRENT_TIME,
744 | &error))
745 | g_warning ("Could not open URL: %s", error->message);
746 | g_clear_pointer (&last_match_text, g_free);
747 | }
748 |
749 |
750 | static const GActionEntry win_actions[] = {
751 | { "font-reset", font_size_action_ativated, "i", "0", NULL },
752 | { "font-bigger", font_size_action_ativated, "i", "1", NULL },
753 | { "font-smaller", font_size_action_ativated, "i", "-1", NULL },
754 | { "copy", copy_action_activated, NULL, NULL, NULL },
755 | { "paste", paste_action_activated, NULL, NULL, NULL },
756 | { "copy-url", copy_url_action_activated, NULL, NULL, NULL },
757 | { "open-url", open_url_action_activated, NULL, NULL, NULL },
758 | };
759 |
760 | static const GActionEntry app_actions[] = {
761 | { "new-terminal", new_terminal_action_activated, NULL, NULL, NULL },
762 | { "about", about_action_activated, NULL, NULL, NULL },
763 | { "quit", quit_action_activated, NULL, NULL, NULL },
764 | };
765 |
766 |
767 | static void
768 | on_child_spawned (G_GNUC_UNUSED VteTerminal *vtterm,
769 | GPid pid, GError *error, void *userdata)
770 | {
771 | if (pid == -1) {
772 | // Error: report and close window.
773 | g_assert_nonnull (error);
774 | gtk_window_close (GTK_WINDOW (userdata));
775 | g_error ("Cannot spawn child process: %s", error->message);
776 | } else {
777 | g_assert_null (error);
778 | }
779 | }
780 |
781 |
782 | static GtkWidget*
783 | create_new_window (GtkApplication *application,
784 | GVariantDict *options)
785 | {
786 | g_autofree char *command = NULL;
787 | g_autofree char *title = NULL;
788 | gboolean opt_show_title;
789 | gboolean opt_update_title;
790 | gboolean opt_no_headerbar;
791 |
792 | g_object_get (dwt_settings_get_instance (),
793 | "show-title", &opt_show_title,
794 | "update-title", &opt_update_title,
795 | "no-header-bar", &opt_no_headerbar,
796 | "command", &command,
797 | "title", &title,
798 | NULL);
799 |
800 | const gchar *opt_command = command;
801 | const gchar *opt_title = title;
802 | const gchar *opt_workdir = NULL;
803 |
804 | if (options) {
805 | gboolean opt_no_auto_title = FALSE;
806 | g_variant_dict_lookup (options, "title-on-maximize", "b", &opt_show_title);
807 | g_variant_dict_lookup (options, "no-header-bar", "b", &opt_no_headerbar);
808 | g_variant_dict_lookup (options, "no-auto-title", "b", &opt_no_auto_title);
809 | g_variant_dict_lookup (options, "workdir", "&s", &opt_workdir);
810 | g_variant_dict_lookup (options, "command", "&s", &opt_command);
811 | g_variant_dict_lookup (options, "title", "&s", &opt_title);
812 | if (opt_no_auto_title)
813 | opt_update_title = FALSE;
814 | }
815 | if (!opt_workdir) opt_workdir = g_get_home_dir ();
816 | if (!opt_command) opt_command = guess_shell ();
817 |
818 | /*
819 | * Title either comes from the default value of the "title" setting,
820 | * or from the command line flag, but should never be NULL at this
821 | * point.
822 | */
823 | g_assert (opt_title);
824 |
825 | g_autoptr(GError) error = NULL;
826 | gint command_argv_len = 0;
827 | gchar **command_argv = NULL;
828 |
829 | if (!g_shell_parse_argv (opt_command,
830 | &command_argv_len,
831 | &command_argv,
832 | &error))
833 | {
834 | g_printerr ("%s: coult not parse command: %s\n",
835 | __func__, error->message);
836 | return NULL;
837 | }
838 |
839 | GtkWidget *window = gtk_application_window_new (application);
840 | gtk_widget_set_visual (window,
841 | gdk_screen_get_system_visual (gtk_widget_get_screen (window)));
842 | gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (window),
843 | FALSE);
844 | gtk_window_set_title (GTK_WINDOW (window), opt_title);
845 | gtk_window_set_hide_titlebar_when_maximized (GTK_WINDOW (window),
846 | !opt_show_title);
847 |
848 | g_action_map_add_action_entries (G_ACTION_MAP (window), win_actions,
849 | G_N_ELEMENTS (win_actions), window);
850 |
851 | VteTerminal *vtterm = VTE_TERMINAL (vte_terminal_new ());
852 | configure_term_widget (vtterm, options);
853 | term_char_size_changed (vtterm,
854 | vte_terminal_get_char_width (vtterm),
855 | vte_terminal_get_char_height (vtterm),
856 | window);
857 |
858 | g_signal_connect (G_OBJECT (window), "notify::has-toplevel-focus",
859 | G_CALLBACK (window_has_toplevel_focus_notified),
860 | vtterm);
861 |
862 | g_signal_connect (G_OBJECT (vtterm), "char-size-changed",
863 | G_CALLBACK (term_char_size_changed), window);
864 | g_signal_connect (G_OBJECT (vtterm), "child-exited",
865 | G_CALLBACK (term_child_exited), window);
866 | g_signal_connect (G_OBJECT (vtterm), "bell",
867 | G_CALLBACK (term_beeped), window);
868 | g_signal_connect (G_OBJECT (vtterm), "button-release-event",
869 | G_CALLBACK (term_mouse_button_released),
870 | setup_popover (vtterm));
871 |
872 | /*
873 | * Propagate title changes to the window.
874 | */
875 | if (opt_update_title)
876 | g_object_bind_property (G_OBJECT (vtterm), "window-title",
877 | G_OBJECT (window), "title",
878 | G_BINDING_DEFAULT);
879 |
880 | if (!opt_no_headerbar)
881 | setup_header_bar (window, vtterm, opt_show_title);
882 |
883 | gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (vtterm));
884 | gtk_widget_set_receives_default (GTK_WIDGET (vtterm), TRUE);
885 |
886 | /* We need to realize and show the window for it to have a valid XID */
887 | gtk_widget_show_all (window);
888 |
889 | gchar **command_env = g_get_environ ();
890 | #ifdef GDK_WINDOWING_X11
891 | if (GDK_IS_X11_SCREEN (gtk_widget_get_screen (window))) {
892 | GdkWindow *gdk_window = gtk_widget_get_window (window);
893 | if (gdk_window) {
894 | gchar window_id[NDIGITS10(unsigned long)];
895 | snprintf (window_id,
896 | sizeof (window_id),
897 | "%lu",
898 | GDK_WINDOW_XID (gdk_window));
899 | command_env = g_environ_setenv (command_env,
900 | "WINDOWID",
901 | window_id,
902 | TRUE);
903 | } else {
904 | g_printerr ("No window, cannot set $WINDOWID!\n");
905 | }
906 | }
907 | #endif /* GDK_WINDOWING_X11 */
908 |
909 | vte_terminal_spawn_async (VTE_TERMINAL (vtterm),
910 | VTE_PTY_DEFAULT,
911 | opt_workdir,
912 | command_argv,
913 | command_env,
914 | G_SPAWN_SEARCH_PATH,
915 | NULL,
916 | NULL,
917 | NULL,
918 | -1,
919 | NULL,
920 | on_child_spawned,
921 | window);
922 | return window;
923 | }
924 |
925 |
926 | static void
927 | app_started (GApplication *application, gpointer userdata)
928 | {
929 | /* Set default icon, and preferred theme variant. */
930 | g_autofree char *cursor_color = NULL;
931 | g_autofree char *icon = NULL;
932 | g_object_get (dwt_settings_get_instance (),
933 | "cursor-color", &cursor_color,
934 | "icon", &icon,
935 | NULL);
936 | gtk_window_set_default_icon_name (icon);
937 | g_object_set(gtk_settings_get_default(),
938 | "gtk-application-prefer-dark-theme",
939 | TRUE, NULL);
940 |
941 | if (cursor_color) {
942 | gdk_rgba_parse (&cursor_active, cursor_color);
943 | memcpy (&cursor_inactive, &cursor_active, sizeof (GdkRGBA));
944 | cursor_inactive.red = 0.5 * cursor_active.red;
945 | cursor_inactive.green = 0.5 * cursor_active.green;
946 | cursor_inactive.blue = 0.5 * cursor_active.blue;
947 | cursor_inactive.alpha = 0.5 * cursor_active.alpha;
948 | }
949 |
950 | image_regex = g_regex_new (image_regex_string, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, 0, NULL);
951 | g_assert (image_regex);
952 |
953 | g_action_map_add_action_entries (G_ACTION_MAP (application), app_actions,
954 | G_N_ELEMENTS (app_actions), application);
955 |
956 | g_autoptr(GtkBuilder) builder = gtk_builder_new_from_resource (DWT_GRESOURCE ("menus.xml"));
957 | gtk_application_set_app_menu (GTK_APPLICATION (application),
958 | G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
959 |
960 | const struct {
961 | const gchar *action;
962 | const gchar *accel;
963 | GVariant *param;
964 | } accel_map[] = {
965 | { "app.new-terminal", "n", NULL },
966 | { "win.font-reset", "0", g_variant_new_int32 (+0) },
967 | { "win.font-bigger", "plus", g_variant_new_int32 (+1) },
968 | { "win.font-smaller", "minus", g_variant_new_int32 (-1) },
969 | { "win.copy", "c", NULL },
970 | { "win.paste", "p", NULL },
971 | };
972 |
973 | for (guint i = 0; i < G_N_ELEMENTS (accel_map); i++) {
974 | gtk_application_add_accelerator (GTK_APPLICATION (application),
975 | accel_map[i].accel,
976 | accel_map[i].action,
977 | accel_map[i].param);
978 | }
979 | }
980 |
981 |
982 | static void
983 | app_shutdown (GApplication *application, gpointer userdata)
984 | {
985 | g_regex_unref (image_regex);
986 | }
987 |
988 |
989 | static gint
990 | app_command_line_received (GApplication *application,
991 | GApplicationCommandLine *cmdline)
992 | {
993 | g_application_hold (G_APPLICATION (application));
994 | GVariantDict *options = g_application_command_line_get_options_dict (cmdline);
995 |
996 | g_autofree char *opt_theme = NULL;
997 | if (g_variant_dict_lookup (options, "theme", "s", &opt_theme) && g_str_equal (opt_theme, "list")) {
998 | for (size_t i = 0; i < G_N_ELEMENTS (themes); i++) {
999 | g_print ("%s\n", themes[i].name);
1000 | }
1001 | } else {
1002 | create_new_window (GTK_APPLICATION (application), options);
1003 | }
1004 | g_variant_dict_unref (options);
1005 | g_application_release (application);
1006 | return 0;
1007 | }
1008 |
1009 |
1010 | static const gchar*
1011 | get_application_id (const gchar* argv0) {
1012 | static const gchar *app_id = NULL;
1013 | if (!app_id) {
1014 | if (!(app_id = g_getenv ("DWT_APPLICATION_ID"))) {
1015 | app_id = "org.perezdecastro.dwt";
1016 | const gchar* slash_position = strrchr (argv0, '/');
1017 | if (slash_position)
1018 | argv0 = slash_position + 1;
1019 | if (!g_str_equal ("dwt", argv0))
1020 | app_id = g_strdup_printf ("%s.%s", app_id, argv0);
1021 | g_set_prgname (app_id + G_N_ELEMENTS ("org.perezdecastro.") - 1);
1022 | }
1023 | }
1024 | return (g_str_equal ("none", app_id) ||
1025 | g_getenv ("DWT_SINGLE_WINDOW_PROCESS") != NULL)
1026 | ? NULL : app_id;
1027 | }
1028 |
1029 |
1030 | int
1031 | main (int argc, char *argv[])
1032 | {
1033 | g_autoptr(GtkApplication) application =
1034 | gtk_application_new (get_application_id (argv[0]),
1035 | G_APPLICATION_HANDLES_COMMAND_LINE);
1036 |
1037 | g_application_add_main_option_entries (G_APPLICATION (application),
1038 | option_entries);
1039 |
1040 | g_signal_connect (G_OBJECT (application), "startup",
1041 | G_CALLBACK (app_started), NULL);
1042 | g_signal_connect (G_OBJECT (application), "shutdown",
1043 | G_CALLBACK (app_shutdown), NULL);
1044 | g_signal_connect (G_OBJECT (application), "command-line",
1045 | G_CALLBACK (app_command_line_received), NULL);
1046 |
1047 | return g_application_run (G_APPLICATION (application), argc, argv);
1048 | }
1049 |
1050 |
--------------------------------------------------------------------------------