xmenu: my first project with xlib - Programming On Unix

Users browsing this thread: 2 Guest(s)
phillbush
Long time nixers
I am learning C and Xlib, and this is my first project with xlib: xmenu.

It is a small utility that receives a menu specification in stdin, shows a menu where you can select one of the options, and outputs the option selected to stdout.

It receives as input a menu specification where each line is a menu entry.
Each line can be indented with tabs to represent nested menus.
Each line is made out of a label and a command separated by tab.
When you select an entry in the menu, the corresponding command is output to the stdout.
It becomes clearer by trying it.

I need some help with xlib, though, if someone here understand it, I will appreciate some help.

EDIT: I updated the program, here is how it looks now:
[Image: xmenu.png]
z3bra
Grey Hair Nixers
I neer used Xlib, but used XCB instead, so I might help if needed !

In fact, I wrote a similar application (xmenu) using XCB. I worked on it mostly to learn about XFT fonts, but if you have any questions, I'll do my best to help you out. It seems like you went pretty far already, given that your software works in the intended way ☺
phillbush
Long time nixers
My question is on drawing on pixmaps.
I am drawing directly on the window, but most code I have seen, such as dmenu(1) and thingmenu(1), all created a pixmap with XCreatePixmap(3), drawn on the pixmap, and then copied the area on the pixmap over the window with XCopyArea(3). Is there any difference from drawing directly on a window than drawing on a pixmap and then copy it to a window?
What are Xlib pixmaps used for if I can draw directly on windows?

Anyways, I have to do some adjustments on my xmenu(1), such as using XFT fonts, use a beter colorscheme, adjust that ugly triangles, add more options, add keyboard handling, reading colors from Xresources, etc.

Did you test it? What did you think about it? I will test your xmenu. Yours receive the options from arguments instead of from stdin, just like thingmenu(1), right?

My next projects will be to create a bar, and them a simple WM.

(PS: I didn't know you also had a menu called xmenu, sorry for copying you...)
venam
Administrators
(14-05-2020, 08:08 PM)phillbush Wrote: What are Xlib pixmaps used for if I can draw directly on windows?
I go into some good explaining of this in this article.

In sum, windows and pixmap are instances of something more abstract called a drawable. The difference is that a window is something on the screen already while a pixmap only lives in a memory buffer. You can then apply the pixmap pixels over some other drawables.
z3bra
Grey Hair Nixers
I didn't test yours, but I like the submenu idea built into it ☺ And don't worry about the name, I chose it because I lack a better name. This is only a toy project and not something big that I plan on sharing/advertising (hence the lack of documentation as well).

Mine works by ordering all arguments into a menu (no submenu support), and print the selected entry to stdout.

I use it like so, where each argument is a command in my $PATH that can be directly executed by the shell.
The ugly "while" loop is needed to fork the commands and make the whole thing asynchronous.
The "-d" flag is meant to daemonize the menu, and have it pop on mouse click events on the root window (just like cwm or rio menus). You click the root window, hold it down, select an entry, and release it to validate.

Code:
xmenu -d \
        'drawterm' \
        'killw $(slw)' \
        'reshape' \
        'snarf' \
        'plumb' \
        'st -e irc' \
        'st -e mutt' \
        | while read x; do sh -c "$x & exit"; done 2>/dev/null
vain
Long time nixers
(15-05-2020, 02:47 AM)venam Wrote:
Quote:What are Xlib pixmaps used for if I can draw directly on windows?
I go into some good explaining of this in this article.

In sum, windows and pixmap are instances of something more abstract called a drawable. The difference is that a window is something on the screen already while a pixmap only lives in a memory buffer. You can then apply the pixmap pixels over some other drawables.

... and one of the main reasons why anyone would want to draw into a pixmap and then copy it to a window is: Double buffering. When you do a lot of drawing operations directly onto a window, it can happen that you see glitches, artifacts, flickering. Even on today's brutally fast machines. So, you first draw an entire frame into a pixmap and then copy the final result to a window. It'll be smoother.
phillbush
Long time nixers
(15-05-2020, 02:47 AM)venam Wrote: In sum, windows and pixmap are instances of something more abstract called a drawable. The difference is that a window is something on the screen already while a pixmap only lives in a memory buffer. You can then apply the pixmap pixels over some other drawables.

Thanks venam! Your post was very informative. I bookmarked it here for the links in the references.
I will check the other linked posts on your blog either. They might come in handy when I venture in the task of creating my own window manager, the next project I want to do in order to learn more Xlib.

(15-05-2020, 09:49 AM)vain Wrote: ... and one of the main reasons why anyone would want to draw into a pixmap and then copy it to a window is: Double buffering. When you do a lot of drawing operations directly onto a window, it can happen that you see glitches, artifacts, flickering. Even on today's brutally fast machines. So, you first draw an entire frame into a pixmap and then copy the final result to a window. It'll be smoother.

I was indeed experiencing some flickering on the windows of my menus while remapping them. I solved it by improving the algorithm that decides when to remap stack of menus to it preform less remapping.
I will rewrite the code to use a pixmap for each window.
venam
Administrators
I gave your code a try.

It seems that all your menu location calculation rely on the position of the parent window relative to the root window and the screen geometry instead of the position of the entries themselves. Thus, you have to keep track of both x and y position and refresh the position of the parent window constantly. I don't think this is the best way to do this, you should only have to keep track of the Y cursor position that is internal to the window, not even the X.

For example, you pass to the getmenuitem function the position of the pointer on the root window instead of the position relative to within the window. Namely, ev.xbutton.x_root and ev.xbutton.y_root instead of ev.xbutton.x and ev.xbutton.y. Those are defined in man 3 XMotionEvent, in the XButtonEvent structure.

In my opinion you'll only have to rely on multiple of the item->y/geom.itemh and in the getmenuitem comparison you could simply do:

Code:
for (item = menu->list; item != NULL; item = item->next) {
    if (y >= item->y && y <= item->y + geom.itemh) {
        *menu_ret = menu;
        *item_ret = item;
        return;
    }
}

There's also an issue in allocitem in which the count doesn't seem to increment properly after the first 2 items, set in parsestdin. Adding count++ after the allocitem seems to do the trick here but it doesn't continue after the first item that has a child. I didn't dig further than that.

I hope this mini overview can help make your menu better.
phillbush
Long time nixers
(15-05-2020, 03:04 PM)venam Wrote: It seems that all your menu location calculation rely on the position of the parent window relative to the root window and the screen geometry instead of the position of the entries themselves. Thus, you have to keep track of both x and y position and refresh the position of the parent window constantly.

The positions are calculated at the beginning and are constant through the entire program, they are not recalculated.

(15-05-2020, 03:04 PM)venam Wrote: For example, you pass to the getmenuitem function the position of the pointer on the root window instead of the position relative to within the window. Namely, ev.xbutton.x_root and ev.xbutton.y_root instead of ev.xbutton.x and ev.xbutton.y. Those are defined in man 3 XMotionEvent, in the XButtonEvent structure.

I read the manuals and notice that, I will change that.

(15-05-2020, 03:04 PM)venam Wrote: I hope this mini overview can help make your menu better.

And it will!
Thanks for all help and support!
PS: xmenu is now on github
phillbush
Long time nixers
Just added xft support for xmenu and had a bad time doing it.

There is no documentation for xft functions and the only manpage that exists (Xft(3)) only has a fast introduction to some of the most used function calls, but not all of them. If it weren't for dmenu's code I'd not know how to use some functions. dmenu source code has really saved me.

Do some of you know a good resource where I can learn more about xft since there is no official manpage nor documentation? Not even the header file has a comment to explain what a function does or what its arguments mean.
venam
Administrators
(18-05-2020, 10:48 PM)phillbush Wrote: Do some of you know a good resource where I can learn more about xft since there is no official manpage nor documentation?

There's a lot of good stuff in man 3 Xft describing all the methods.
I faced the same issue with fontconfig, it's not documented well, only the functions signature and the type they take. Take a look here for example. So what you end up doing is detective work, matching and testing different functions.

Take note that Xft, fontconfig, and a bunch of other libs where all written by the small cluster of persons, especially Keith Packard. That's why everyone uses the higher level libraries such as pango instead of relying on the lower level ones. Take a look at this.
phillbush
Long time nixers
I've just noticed that xmenu got featured in a video by a famous Linuxer youtuber: here.

It was my first project and it is very nice to have it featured by a popular youtuber. But it's also scary since xmenu was such a small project and now there are a lot of people using it and wanting more from it.
And now that it is more popular, there are issues being reported and features being requested.
The most requested feature is to add support for fallback fonts (more than one font), but it is a pita to do with plain xft. I've just looked at the code of Suckless' dmenu and its function for drawing text with multiple fonts is gigantic.
I will probably rather use pango or move xmenu to xcb and use venam's fonts for xcb.

But overall I got very excited when they called me: "hey, have you watched the last DT's video about xmenu?".
I'm just sharing my euphoria in this post, hehe.
opFez
Members
(14-05-2020, 06:45 PM)phillbush Wrote: It is a small utility that receives a menu specification in stdin, shows a menu where you can select one of the options, and outputs the option selected to stdout.

So it's basically dmenu but for the mouse? Seems very useful!