User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Portable GUI C89 Programming.

Tue Oct 04, 2016 10:58 pm

Ok I have decided to take a little time out of my main programming project to put together some good examples of how to do C Programming in a portable and simple way with out the use of any toolkits, though yet still implementing some fair amount of usage of the GUI.

The purpose here is to show how much simpler and faster it is to do it this way, while this will not be anywhere near as advanced as is reasonable with this method it should be a good point maker.

Probably should have waited for this post till I got the code done, though I did not wish to forget.

This is in responce to a couple of people that keep pushing the falicy that using toolkits always saves time. Simply not true, now using wrappers does save time, as does keeping gui function code libraries.

So I will update as I write some good example code.

Of cource the samples I provide will be to compile for RISC OS, though there will be good instructions for porting (thus showing how easy it is if you keep the target platform code libraries around).

I realized that this is as good of a time as any to make my x11 version of the UI functions I use. So it is that I have redirected myself, sorry for the delay.

EDIT: Corrected ommision.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 3:52 am

I realized that this is a good time to finish learning programming for X, so I am going to do my example targetting X.

This will even more show how easy it is, yes it takes a little time to learn and take notes for a GUI that is new to you, though when considering the time you later save in attempting to port it (or worse the time you save compared to using the toolkits), it is well worth the little bit of time spent to learn a new GUI.

I know very little of X Windowing System program so this is about as close to a completely clean slate as you can get :) .
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 7:43 am

This is in responce to a couple of people that keep pushing the falicy that using toolkits always saves time.
That would be me.

This should be interesting.

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 7:46 am

Heater wrote: That would be me.
<AOL> Me too !! </AOL>
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:35 am

Everything you need to know about Xlib programming is very nicely described here: http://tldp.org/LDP/LG/issue78/tougher.html

Of course he uses C++ so you won't like that much. But it does allow him to easily wrap up the GUI stuff into reusable code. Although his abstractions are heavily Xlib dependent still.

I say "Everything you need to know about Xlib programming" because creating a simple button on Xlib takes about 400 lines of code. http://tldp.org/LDP/LG/issue78/misc/tou ... on.hpp.txt Having got that far your realize that you have already read more about Xlib than you ever wanted to!

At which point you turn to something that will get the job done quicker. Like Qt:

Code: Select all

QPushButton *button = new QPushButton("&Download", this);
Which is also usable on Windows, Mac, Linux frame buffer, Android and places I have never heard of.

jahboater
Posts: 4595
Joined: Wed Feb 04, 2015 6:38 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 9:16 am

The manual:
https://www.x.org/releases/X11R7.7/doc/ ... libX11.pdf

Using a standard that's already 27 years out of date is a bad idea (C89) even if some modern compilers still support it. They will continue to support C11 for much longer.

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 10:47 am

Should I lend these from 1990 to DavidS ?
Image
Been there, done that....
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
Paeryn
Posts: 2614
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 12:45 pm

Ohh, not seen those books in a long time. I remember spending a weekend in the uni library going through them trying to figure something out in the early 90s.
She who travels light — forgot something.

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 2:33 pm

Using XCB, not XLib :).

Though I do admit that it is a bit more involved than any other GUI I have yet worked with, and taking longer to learn than any other (I started studying last night and can only create a window, do some basic drawing in the window and track the mouse, by now I should be able to write a complete app, not with X).
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 3:23 pm

WOW
Writing for X (using XCB which is as low level as you can go with out manually making connections to the X Server) is truly terrible. Though this is the reason for generalized code libraries.

It would actually take less work to write a window manager running directly on the Linux Framebuffer than it does to write a complete UI for X. This being because in X you have to manually draw everything using the graphics primitives provided by X, which are not that well designed, they take a good deal of work to use, more so than drawing the same to a frame buffer, even once you add clipping, rect management, layered windowing, menu libraries, widget libraries, and pixel mapped raster graphics to your framebuffer code.

I must admit that I am impressed at how bad X is to program for, and this is going to take longer to complete than I ever expected. Though once completed it will save a lot of time porting any of my existing code to X.
Heater wrote:Everything you need to know about Xlib programming is very nicely described here: http://tldp.org/LDP/LG/issue78/tougher.html

Of course he uses C++ so you won't like that much. But it does allow him to easily wrap up the GUI stuff into reusable code. Although his abstractions are heavily Xlib dependent still.
I have no trouble with C++, it is a decent language.

And my UI code collection is OO, though done in strait C (yes you can do OO in strait C if you understand OO well enough).
I say "Everything you need to know about Xlib programming" because creating a simple button on Xlib takes about 400 lines of code. http://tldp.org/LDP/LG/issue78/misc/tou ... on.hpp.txt Having got that far your realize that you have already read more about Xlib than you ever wanted to!
Drawing a button in X can be done in 7 lines of code, the mouse tracking can be done in 5 lines of code.

X does not handle buttons, that is on you to draw and use how you want.

I am realizing that a simple widget toolkit has its place in X though so far only in X. So after I complete this challenge I may actually use FLTK (only FLTK because it is a simple widget toolkit, unlike Qt/GTK/etc).

Though likely not as I will already have the code library started :) . One nice thing about X is it does not force you to do anything at all any way, it is all up to the programmer.
At which point you turn to something that will get the job done quicker. Like Qt:

Code: Select all

QPushButton *button = new QPushButton("&Download", this);
Which is also usable on Windows, Mac, Linux frame buffer, Android and places I have never heard of.
Qt does a lot before your call gets to the system, that is not what I want.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 4:40 pm

Found better information on XCB Programming:
https://www.x.org/releases/X11R7.6-RC1/ ... index.html

It appears I was attempting to do a lot the harder way for XCB. So I have to redo my GUIFn.c to do things the easier way. Still means drawing all of my own controls, though a lot easier than what I was doing :) .

And by the looks of it the GUIFn.c for using the X Windowing System will be less than 200 lines of code (maybe just over 200 lines, we will see) I am naming the file GUIFnXW. Of course the GUIFn.h is the same for all platforms, that is kind of the idea.

Now that I am actually getting somewhere some information is in order:

NOTE: I am using DOS/Atari style path names, modify path names for the target system if needed.
In order to use the gui routines for C you need to includude GUIFn.h in the source modules that use the GUI routines. You will need to copy the appropriate version of the C file to your source directory, and include it in the compile command line. For example if using Atari GEM and and targeting Atari GEM you have the GUI routines in the directory C:\Proj\GUI you would do the following (from your source directory):

Code: Select all

copy C:\Proj\GUI\GUIFnAGM.c
copy C:\Proj\GUI\h\GUIFn.h
cc -o progname.prg main.c myother.c another.c whatever.c GUIFnAGM.c
Replacing what cc with the compiler command that you are using (will work with AHCC, newer versions of Pure C, and GCC).

Of course for this example you would have in the top of each C module the line:

Code: Select all

#include "GUIFn.h"
The only change for other platforms is the version of the GUIFn file used, GUIFnW32 for MS-Windows 32 bit, GUIFnMac.c for Macintosh System Software (designed with the use of Macintosh SSW 6.0.4 through MAC OS 8.1 in mind), etc. Pretty simple.

Of course you do have to create what ever new platform targeting versions of GUIFn.c you need, though it ends up saving a lot of time in in the end, as it is easier than any of these over marketed GUI toolkits, and it takes a lot less system resources to use than these GUI toolkits.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
scruss
Posts: 2363
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 4:52 pm

DavidS wrote:It would actually take less work to write a window manager running directly on the Linux Framebuffer than it does to write a complete UI for X.
Yes, of course it would. But that's not what X is for. X was designed to abstract the display completely from the computer, so you could have a big mainframe talking to lots of relatively inexpensive X terminals. The difficulty with using the Linux framebuffer is not merely would you have to maintain code for the quirks of thousands of different displays, but you'd have to go through all the internationalization* and security issues that X was forced to solve years ago. So yes, X is slow and a bit nasty and creates lots of problems, but it has also solved a lot of problems that would take lifetimes of programming to fix.

I don't think I've ever seen an attempt at a direct framebuffer UI on Linux; it would most likely have to run as root, which is a big old no from the start. I do remember dreadful predecessors to X like Suntools on 68k-based Sun workstations (as well as the dismal Sun386i) and I'd rather not go back there.

*: the hard ones, anyway, like mixed-direction text: text width is not always positive (see Hebrew, Arabic) and it's not always on a horizontal baseline (see Japanese).
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 5:25 pm

scruss wrote:
DavidS wrote:It would actually take less work to write a window manager running directly on the Linux Framebuffer than it does to write a complete UI for X.
Yes, of course it would. But that's not what X is for. X was designed to abstract the display completely from the computer, so you could have a big mainframe talking to lots of relatively inexpensive X terminals. The difficulty with using the Linux framebuffer is not merely would you have to maintain code for the quirks of thousands of different displays, but you'd have to go through all the internationalization* and security issues that X was forced to solve years ago. So yes, X is slow and a bit nasty and creates lots of problems, but it has also solved a lot of problems that would take lifetimes of programming to fix.

I don't think I've ever seen an attempt at a direct framebuffer UI on Linux; it would most likely have to run as root, which is a big old no from the start. I do remember dreadful predecessors to X like Suntools on 68k-based Sun workstations (as well as the dismal Sun386i) and I'd rather not go back there.

*: the hard ones, anyway, like mixed-direction text: text width is not always positive (see Hebrew, Arabic) and it's not always on a horizontal baseline (see Japanese).
True the library providing the access to the Framebuffer would have to be root in order to access the FB. Though it could export an API that is accessible to user level code, as it would be a library if it is providing services for other applications.

Yes X did solve the text direction issues, as has almost every other GUI that survives. Some of the character sets are more of a chalenge (Aribic the form of a character can depend on the preceding character, following character and the tone of the spoken char, as well as the length of pronunciation).

Thought there have been contest entries for creating the under 1000 line complete GUI in less than a month (or similar small GUI host contests) that have created GUI hosts providing a complete API and supporting western locals, as well as Aribic, Hebrew, and other left to right scripts. So it is possible for a single person to implement such a thing that is quite complete. Most of the contests that such systems have been created for require that the GUI provide support for overlapping windows, sprites, bitmapped images, buttons, menus, standard window widgets, resizable windows, and multi font support.

The reason it was so much work for X is the interface that is designed for remote display of graphics applications.

In the modern world a GUI that is meant for local use should be written as such, rather than using something like a local X server. We generally no longer use X to remotely display graphical applications, using something like a VNC implementation instead, so there is no longer any need for the GUI to provide the services for remote display of an applications output, and remote input to the application, yet we still use X for some unknown reason.

To show how simple a distribution level GUI host that is designed for local use and is 100% customizable in all ways (except the API of course), and supports full internationalization, look at the current implementations of fVDI + XaAES + Thing Desktop running on MiNT (the Atari 32-Bit OS). That provides a GUI with all of the features of any modern GUI, and does so in a very small amount of code compared to any other GUI I know of that is widely distributed, and has as many features. Yes it is a stacking window manager, that should not matter, a compositing window manager can actually be made smaller than a stacking window manager, though a compositing window manager uses a lot more memory and CPU time than a compositing window manager.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 5:31 pm

I am afraid this is going to take some time. I was not expecting to have to write an entire widget library, just for a simple text editor that can shell out a compiler and has syntax highlighting, and a simple calculator application :( .
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 7:46 pm

DavidS
Drawing a button in X can be done in 7 lines of code, the mouse tracking can be done in 5 lines of code.
Hmm...12 lines of code to make a button in X?

The xlib example I linked to above was, what, 400 lines?

Turns out xcb is lean. A window and a single button can be made in 308 lines. It looks like this:

Image

and this is the code. Which 296 lines do you think can be removed from that?

Code: Select all

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <inttypes.h>

    #include <xcb/xcb.h>

    #define WIDTH 300 
    #define HEIGHT 150 

    static void testCookie(xcb_void_cookie_t, xcb_connection_t*, char *); 
    static void drawButton(xcb_connection_t*, xcb_screen_t*, xcb_window_t, int16_t, int16_t, const char*);
    static void drawText(xcb_connection_t*, xcb_screen_t*, xcb_window_t, int16_t, int16_t, const char*);
    static xcb_gc_t getFontGC(xcb_connection_t*, xcb_screen_t*, xcb_window_t, const char*);
    static void setCursor (xcb_connection_t*, xcb_screen_t*, xcb_window_t, int);

    /*  
    */  
    static void
    testCookie (xcb_void_cookie_t cookie,
                xcb_connection_t *connection,
                char *errMessage )
    {   
        xcb_generic_error_t *error = xcb_request_check (connection, cookie);
        if (error) {
            fprintf (stderr, "ERROR: %s : %"PRIu8"\n", errMessage , error->error_code);
            xcb_disconnect (connection);
            exit (-1);
        }   
    }   

    /*  
    */  
    static void
    drawButton (xcb_connection_t *connection,
                xcb_screen_t     *screen,
                xcb_window_t      window,
                int16_t           x1, 
                int16_t           y1, 
                const char       *label )
    {   
        uint8_t length = strlen (label);
        int16_t inset = 2;
        int16_t width = 7 * length + 2 * (inset + 1); 
        int16_t height = 13 + 2 * (inset + 1); 

        xcb_point_t points[5];
        points[0].x = x1; 
        points[0].y = y1; 
        points[1].x = x1 + width;
        points[1].y = y1; 
        points[2].x = x1 + width;
        points[2].y = y1 - height;
        points[3].x = x1; 
        points[3].y = y1 - height;
        points[4].x = x1; 
        points[4].y = y1; 

        xcb_gcontext_t gc = getFontGC (connection, screen, window, "fixed");
        xcb_void_cookie_t lineCookie = xcb_poly_line_checked (connection,
                                                              XCB_COORD_MODE_ORIGIN,
                                                              window,
                                                              gc,
                                                              5,
                                                              points );
        testCookie (lineCookie, connection, "can't draw lines");

        xcb_void_cookie_t textCookie = xcb_image_text_8_checked (connection,
                                                                 length,
                                                                 window,
                                                                 gc,
                                                                 x1 + inset + 1,
                                                                 y1 - inset - 1,
                                                                 label );
        testCookie (textCookie, connection, "can't paste text");

        xcb_void_cookie_t gcCookie = xcb_free_gc (connection, gc);
        testCookie (gcCookie, connection, "can't free gc");
    }

    /*
    */
    static void
    drawText (xcb_connection_t *connection,
              xcb_screen_t     *screen,
              xcb_window_t      window,
              int16_t           x1,
              int16_t           y1,
              const char       *label )
    {

        xcb_gcontext_t gc = getFontGC (connection, screen, window, "fixed");
        xcb_void_cookie_t textCookie = xcb_image_text_8_checked (connection,
                                                                 strlen (label),
                                                                 window,
                                                                 gc,
                                                                 x1,
                                                                 y1,
                                                                 label );
        testCookie(textCookie, connection, "can't paste text");

        xcb_void_cookie_t gcCookie = xcb_free_gc (connection, gc);
        testCookie (gcCookie, connection, "can't free gc");
    }

    /*
    */
    static xcb_gc_t
    getFontGC (xcb_connection_t *connection,
               xcb_screen_t     *screen,
               xcb_window_t      window,
               const char       *fontName )
    {

        xcb_font_t font = xcb_generate_id (connection);
        xcb_void_cookie_t fontCookie = xcb_open_font_checked (connection,
                                                              font,
                                                              strlen (fontName),
                                                              fontName );
        testCookie (fontCookie, connection, "can't open font");

        xcb_gcontext_t gc = xcb_generate_id (connection);
        uint32_t  mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
        uint32_t value_list[3];
        value_list[0] = screen->black_pixel;
        value_list[1] = screen->white_pixel;
        value_list[2] = font;

        xcb_void_cookie_t gcCookie = xcb_create_gc_checked (connection,
                                                            gc,
                                                            window,
                                                            mask,
                                                            value_list );
        testCookie (gcCookie, connection, "can't create gc");

        fontCookie = xcb_close_font_checked (connection, font);
        testCookie (fontCookie, connection, "can't close font");

        return gc;
    }

    /*
    */
    static void
    setCursor (xcb_connection_t *connection,
                xcb_screen_t     *screen,
                xcb_window_t      window,
                int               cursorId )
    {
        xcb_font_t font = xcb_generate_id (connection);
        xcb_void_cookie_t fontCookie = xcb_open_font_checked (connection,
                                                              font,
                                                              strlen ("cursor"),
                                                              "cursor" );
        testCookie (fontCookie, connection, "can't open font");

        xcb_cursor_t cursor = xcb_generate_id (connection);
        xcb_create_glyph_cursor (connection,
                                 cursor,
                                 font,
                                 font,
                                 cursorId,
                                 cursorId + 1,
                                 0, 0, 0, 0, 0, 0 );

        xcb_gcontext_t gc = xcb_generate_id (connection);

        uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
        uint32_t values_list[3];
        values_list[0] = screen->black_pixel;
        values_list[1] = screen->white_pixel;
        values_list[2] = font;

        xcb_void_cookie_t gcCookie = xcb_create_gc_checked (connection, gc, window, mask, values_list);
        testCookie (gcCookie, connection, "can't create gc");

        mask = XCB_CW_CURSOR;
        uint32_t value_list = cursor;
        xcb_change_window_attributes (connection, window, mask, &value_list);

        xcb_free_cursor (connection, cursor);

        fontCookie = xcb_close_font_checked (connection, font);
        testCookie (fontCookie, connection, "can't close font");
    }

    /*
    */
    int
    main ()
    {
        /* get the connection */
        int screenNum;
        xcb_connection_t *connection = xcb_connect (NULL, &screenNum);
        if (!connection) {
            fprintf (stderr, "ERROR: can't connect to an X server\n");
            return -1;
        }

        /* get the current screen */

        xcb_screen_iterator_t iter = xcb_setup_roots_iterator (xcb_get_setup (connection));

        /* we want the screen at index screenNum of the iterator */
        for (int i = 0; i < screenNum; ++i) {
            xcb_screen_next (&iter);
        }

        xcb_screen_t *screen = iter.data;

        if (!screen) {
            fprintf (stderr, "ERROR: can't get the current screen\n");
            xcb_disconnect (connection);
            return -1;
        }


        /* create the window */

        xcb_window_t window = xcb_generate_id (connection);
        uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
        uint32_t values[2];
        values[0] = screen->white_pixel;
        values[1] = XCB_EVENT_MASK_KEY_RELEASE |
                    XCB_EVENT_MASK_BUTTON_PRESS |
                    XCB_EVENT_MASK_EXPOSURE |
                    XCB_EVENT_MASK_POINTER_MOTION;

        xcb_void_cookie_t windowCookie = xcb_create_window_checked (connection,
                                                                    screen->root_depth,
                                                                    window,
                                                                    screen->root,
                                                                    20, 200, WIDTH, HEIGHT,
                                                                    0,
                                                                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                                                    screen->root_visual,
                                                                    mask, values );
        testCookie (windowCookie, connection, "can't create window");

        xcb_void_cookie_t mapCookie = xcb_map_window_checked (connection, window);
        testCookie (mapCookie, connection, "can't map window");

        setCursor (connection, screen, window, 68);

        xcb_flush(connection);

        /* event loop */

        uint8_t isHand = 0;

        while (1) {
            xcb_generic_event_t *event = xcb_poll_for_event (connection);
            if (event) {
                switch (event->response_type & ~0x80) {
                    case XCB_EXPOSE: {
                        char *text = "click here to change cursor";
                        drawButton (connection,
                                    screen,
                                    window,
                                    (WIDTH - 7 * strlen(text)) / 2,
                                    (HEIGHT - 16) / 2,
                                    text );

                        text = "Press ESC key to exit...";
                        drawText (connection,
                                  screen,
                                  window,
                                  10,
                                  HEIGHT - 10,
                                  text );
                        break;
                    }
                    case XCB_BUTTON_PRESS: {
                        xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;

                        int length = strlen ("click here to change cursor");
                        if ((press->event_x >= (WIDTH - 7 * length) / 2) &&
                                (press->event_x <= ((WIDTH - 7 * length) / 2 + 7 * length + 6)) &&
                                (press->event_y >= (HEIGHT - 16) / 2 - 19) &&
                                (press->event_y <= ((HEIGHT - 16) / 2))) {
                            isHand = 1 - isHand;
                        }

                        if (isHand) {
                            setCursor (connection, screen, window, 58);
                        }
                        else {
                            setCursor (connection, screen, window, 68);
                        }
                    }
                    case XCB_KEY_RELEASE: {
                        xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event;

                        switch (kr->detail) {
                            /* ESC */
                            case 9:
                                free (event);
                                xcb_disconnect (connection);
                                return 0;
                        }
                    }
                }
                free (event);
            }
        }

        return 0;
    }
Attachments
xcb-button.png
xcb-button.png (3.97 KiB) Viewed 2543 times

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:03 pm

Heater,
Minor quibble, it doesn't catch and handle the window being closed.
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:23 pm

PeterO,

Ha! Damn, I did not check for that.

I just fired up Linux in a virtual machine, got the code compiled (It comes from https://xcb.freedesktop.org/), ran it, took the snapshot, posted my post and shut down the VM.

I guess it needs another 50 lines or so :)

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:33 pm

Actually I think there is at least one missing "break" in the event loop as well.
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:51 pm

Boy, should I put that up on github so everyone can contribute fixes and changes?

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 8:56 pm

Heater wrote:PeterO,
I guess it needs another 50 lines or so :)
Actually it's only about 6 lines :-)

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
Paeryn
Posts: 2614
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 9:22 pm

I don't like the look of having the button be just drawn onto and handled by the main window code. Don't know if that's and xcb thing.

Normally you'd create each button (and any other GUI element) as a child windows. That way X can give you information directly according to the element's requirements. Then only elements that need to respond to button presses get button press events (if the main window doesn't request button press events then you'll never get them unless you press over a child window that does).
And it lets X tell you when things happen like the pointer entering / leaving a button without you having to constantly check the pointer's location etc. so you can have things like a button highlighting itself or changing cursor shape whenever the pointer moves within its boundary quite easily.

Then again it's been a long time since I needed to do anything other than basic xlib.
She who travels light — forgot something.

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 9:33 pm

I think it's just a very simple example...
Which does have some errors in the event loop. There are missing "break" , probably due to the poor layout :roll:
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Heater
Posts: 12976
Joined: Tue Jul 17, 2012 3:02 pm

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 9:33 pm

Paeryn,

Oh boy, oh boy. How may thing wrong with this button are there?

Personally I was hoping someone would suggest how to de-uglify it by getting the text centered properly.

Of course we are going to need rounded corners, colour, a gradient perhaps, some hint of three dimensionality.

This button could take thousands of lines of code....

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 9:39 pm

How much of that code is actually drawing the button an checking the bounds of the mouse on a click event and responding that is in the bounds of the button?

It is not that much, and there are better ways to do it, take a look at the linked xcb tutorial.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

swampdog
Posts: 221
Joined: Fri Dec 04, 2015 11:22 am

Re: Portable GUI C89 Programming.

Wed Oct 05, 2016 11:10 pm

One of the most simple and effective GUI systems was Digital Research's GEM, as used by early Apple Mac and Atari ST micros.

There was no GUI per se. What you had was an array of objects in a tree structure, drawn in a given order, with a simple clipping rectangle. It could be created manually, in code (or more quickly using a resource editor (early RAD tool)).

It was bound together by a message queue/pump from which ordered messages could be extracted by the app. It was co-operative and moreover, character based (with pixel offsets) so could theoretically be used both on text and graphical displays.

If one were to take that principle, wrap up the "GUI" part in its own thread so the app did not have to care then mapped the lot onto SDL (because it exists in lots of places) and ncurses (ditto) you'd have a "portable" GUI, especially if it were restricted to monospace fonts.

All the GUI part had to do was track the cursor/mouse and create messages saying what object number was clicked on (and how) and subsequently make state changes to designated objects.

After all, if you want much more out of such a GUI you may was use what's already been written, such as QT, which can be entirely hand coded should you wish, in any case.

Now that I've set the pigeon amongst the cats I shall run off! :-)

Return to “Other programming languages”