Have you ever heard of xmessage, xclock, xcalc, xman, etc.? They are GUI programs of the X Window System, which have been fading into obscurity. I guess people are driven away by their barebones look (although I love it), so most are replaced by "modern" (read: rounded-cornered, soft-colored, antialiased) counterparts.
Which is a shame, because the GUI of the old school X programs is almostly
entirely customizable. All accept
-bg background-color and
-fg foreground-color arguments, even if it is not listed
in their manual pages — it's listed in
man 7 X. But
that is not the end of it. Every widget in the GUI can be customized,
although finding out how can be admittedly not obvious for the
uninitiated. This article will hopefully lower the entrance barrier.
Editres is a GUI editor for the X toolkit applications.
editres and then run
xmessage -buttons button1,button2,button3 'some message'
In Editres' window, go to Commands > Get tree. The cursor will turn into a + sign. Select the Xmessage window and Editres will load Xmessage's widget tree.
Go to Tree > Select widget in client (this action can also be triggered by pressing w, but the cursor needs to be on the Editres window for it to work). The cursor will again turn into a + sign. Click "button1" in the Xmessage window, and it should flash.
If instead Editres says "This widget no longer exists in the client", you need to close Xmessage and try again. I don't know why this happens.
Once it works, you will see "button1" highlighted in Editres.
Go to Commands > Show resource box. A new window will appear with all the button widget's resources. Select background, type "blue" (hexadecimal RGB "#0000FF" is also accepted) into the text field at the bottom and hit apply. Button1 becomes blue!
What if we want to change the font? You can select one from
xlsfonts (CLI) or
I recommend the last one because you can see the options by selecting from dropdowns, and when you are done you can click
select to copy the font name to the primary selection. Then just middle-click (or shift+insert) in Editres'
text field pastes it.
What if I want to get rid of the scrollbar? All the text is visible after all. Close the resources window and repeat the steps to select the "message" widget. Then set scrollVertical to "never".
Most applications whose widget tree Editres can load have in common that their interface is based on the X Athena Widgets library, the first widget set for the X Window System. GTK and Qt are way more popular alternatives today.
Each widget class (text, scrollbar, viewport etc.) has its set of resources (a.k.a X resources), which determine how the widget behaves. These resources have, of course, default values, but both the programmer (when writing the code) and the user can pick different ones. And this is where customization enters.
Back to our example, if you go to Tree > Show class names, you see the class of each mapped widget.
As we see, class names start with a capital letter, while the instance names usually don't. Also, Xaw uses Command as the class name of what we would generally call buttons.
The resources of each widget are found in the X Consortium Standard: Athena Widget Set - C Language Interface. For example, borderWidth is a Dimension value (essentially an integer) that defaults to 1 in most widgets (the grip widget being an exception).
This web page with the same title as the aforementioned document is not as complete as the latter. I only realized that when writing this article. :)
The resource scrollVertical is thus described in the previous document:
A converter is registered for this resource that will convert the following strings: always, never, and whenNeeded. If XawtextScrollWhenNeeded is specified, the appropriate scrollbar will only appear when there is text in the buffer that is not able to fit within the bounds of the current window. The scrollbar will disappear when the text once again fits within the window.
Now, if you try whenNeeded, you will find that actually the scrollbar is never displayed, even if the text doesn't
completely fits the text widget. This happens to be explained differently and correctly in
The value XawtextScrollWhenNeeded (and whenNeeded, recognized by the converter), is accepted for backwards compatibility with resource specifications written for the Xaw6 Text widget, but ignored (effectively treated as XawtextScrollNever)
So it seems the documentation is a bit outdated. I have rarely found outdated bits in it, but you have been warned.
Editing the resources of the Xmessage window with Editres only works for that single window. If you want your changes to take effect for all windows of a given class, you need to tell them to the X server resources database, which usually goes simply by the name of X resources.
Traditionally, you put the resources specifications in
~/.Xresources and then
load them into the X server database with
A sample, reasonably complex fragment of my X resources file is
XFontSel*font: -*-terminus-*-*-*-*-14-* XFontSel*quitButton.mappedWhenManaged: false XFontSel*ownButton.horizDistance: -43 XFontSel*ownButton.label: Own Primary XFontSel*ownButton.shapeStyle: rectangle XFontSel*ownButton.background: yellow XFontSel*commandBox.background: forestgreen XFontSel*commandBox.countLabel.background: forestgreen XFontSel*commandBox.countLabel.foreground: yellow XFontSel*fieldBox*background: deepskyblue XFontSel*fontName.background: deepskyblue XFontSel*sampleText.background: black XFontSel*sampleText.foreground: green XFontSel*showGrip: false
The first line is clear: It sets the font used by the widgets.
The second one says that the quit button not be mapped when managed (jargon for: don't display the widget). Another way of getting the same net result would be to set its horizDistance to a negative value, so that it is shoved off the window.
Then I have to adjust for the "copy to primary selection" button position, since unmapping the quit button leaves its gap behind. I do that by setting a negative horizDistance to it.
Simply setting the quit button width to zero doesn't quite work because the label of the widget forces it to a fitting width. And even if you set the label to the empty string, you still need to zero the borderWidth. So I find my way easier.