Nuklear icon indicating copy to clipboard operation
Nuklear copied to clipboard

How can I get a barebones example (just a window)?

Open mavavilj opened this issue 3 years ago • 26 comments
trafficstars

How can I get a barebones example (just a window)?

Can I e.g. comment out all #ifdefs in the demos?

mavavilj avatar Mar 14 '22 09:03 mavavilj

How can I get a barebones example (just a window)?

Do you mean an os-window or Nuklear window? If os-window, then it has nothing to do with Nuklear and is backend-specific (you mentioned GLFW elsewhere, so feel free to take a look at demo/...glfw... example backends to see how it creates os-windows).

If you meant Nuklear window, then you simply do what's in the readme - e.g. call nk_begin() :wink:.

Can I e.g. comment out all #ifdefs in the demos?

I think this is a too general question - some ifdefs are needed and some are optional. So nothing is stopping you from commenting them (it's a matter of seconds to try it yourself) but expect something might brake.

dumblob avatar Mar 14 '22 10:03 dumblob

Btw. @Ohjurot is probably currently working on some shim library to allow easy creation of quazi-native windows leveraging os-windowing capabilities.

dumblob avatar Mar 14 '22 10:03 dumblob

How can I get a barebones example (just a window)?

Do you mean an os-window or Nuklear window? If os-window, then it has nothing to do with Nuklear and is backend-specific (you mentioned GLFW elsewhere, so feel free to take a look at demo/...glfw... example backends to see how it creates os-windows).

If you meant Nuklear window, then you simply do what's in the readme - e.g. call nk_begin() wink.

Can I e.g. comment out all #ifdefs in the demos?

I think this is a too general question - some ifdefs are needed and some are optional. So nothing is stopping you from commenting them (it's a matter of seconds to try it yourself) but expect something might brake.

This would suggest that the demos are not well encapsulated, if there's no "draw a window" example. Usually libraries like these should have a "hello world" example, in my opinion. For example OpenGL has this kind of basic example: https://www.badprog.com/c-opengl-hello-world. The example given in the readme is useless as such:

https://github.com/Immediate-Mode-UI/Nuklear/issues/422

mavavilj avatar Mar 14 '22 11:03 mavavilj

I mean, I think this example:

https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/main.c

would be really useful, if it had only the X11 part and the Nuklear part that contribute to opening a window, but not drawing anything else on it.

Since I have not written that code, and since I'm not an experienced X11 developer, then now it seems like quite a mess to have to figure out what parts I can delete so that I am left with just the window.

I think that the author of that code would know pretty quickly which parts to delete.

mavavilj avatar Mar 14 '22 11:03 mavavilj

This would suggest that the demos are not well encapsulated, if there's no "draw a window" example.

Hm, I see basically all demos to "draw a window". What do you mean by "no draw a window" example then?

Usually libraries like these should have a "hello world" example, in my opinion.

Please refer to examples/ directory.

For example OpenGL has this kind of basic example: https://www.badprog.com/c-opengl-hello-world. The example given in the readme is useless as such:

#422

This is a huge misunderstanding. Please note you're not dealing with restricted technology here. Nuklear does not need any backend!

The example in readme shows exactly that - that the library is fully pluggable and doesn't dictate absolutely anything as how the underlying backend (e.g. GLFW or a static image) should work nor as how the overlaying system (e.g. a game) should work. I know it's hard to grasp for those coming from badly architectured UI ecosystems. But let's forget it for a while and dive into the zen of Nuklear :wink:.

From this also follows that there can't be any better representative example covering all (or at least major) build systems on all (or at least major) platforms with all (or at least major) backends etc. Nuklear users shall understand that the concept of Nuklear is very different, much simpler, much more lightweight, and much more adaptable.

Nuklear can be used to render to anything - incl. framebuffers, image format files, movie sequences, web canvas, web HTML, etc. No restrictions unlike 99.9% of UI libs out there. This everything with 0 lines of code changes to Nuklear. Show me any other library which can do that.

dumblob avatar Mar 14 '22 11:03 dumblob

I would just like an example that draws a window and nothing else.

The example:

https://github.com/Immediate-Mode-UI/Nuklear#example

does not even look like the same as the code in e.g. the X11 example:

https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/main.c#L153

mavavilj avatar Mar 14 '22 11:03 mavavilj

I mean, I think this example:

https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/main.c

would be really useful, if it had only the X11 part and the Nuklear part that contribute to opening a window, but not drawing anything else on it.

Since I have not written that code, and since I'm not an experienced X11 developer, then now it seems like quite a mess to have to figure out what parts I can delete so that I am left with just the window.

I think that the author of that code would know pretty quickly which parts to delete.

Hm, I have to admit that that example is so simple and short and well enough commented that if anything simpler is desired than I'd highly recommend to those feeling this desire rather to sit down for an hour or two and study it to its very details. Because the same knowledge is necessary for any other widespread system out there I know of (except that in other systems it's much more hidden and thus not just an hour or two but N-times that much).

dumblob avatar Mar 14 '22 11:03 dumblob

I'm not sure if it's that simple, since Nuklear seems to have its own wrapper to Xlib:

https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/nuklear_xlib.h

So to use Nuklear with X11 I'd also need to understand that rather than standard Xlib?

https://tronche.com/gui/x/xlib-tutorial/2nd-program-anatomy.html

mavavilj avatar Mar 14 '22 11:03 mavavilj

Also, there's a good tutorial for the Windows window:

https://github.com/Immediate-Mode-UI/Nuklear/wiki/GDI

Why is there no such for X11?

mavavilj avatar Mar 14 '22 11:03 mavavilj

I'm not sure if it's that simple, since Nuklear seems to have its own wrapper to Xlib:

https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/nuklear_xlib.h

Please read carefully what I wrote above in https://github.com/Immediate-Mode-UI/Nuklear/issues/424#issuecomment-1066674233 - espacially the part about the underlying backend and overlaying system.

First and foremost it's not part of Nuklear. Nuklear is just nuklear.h. Period. Nothing more nothing less.

Second the code you're referring to is not a wrapper (which is apparent from the source code of nuklear_xlib.h). It's just yet another backend renderer for Nuklear. There are other backend renderers using X11 (which is another proof why neither of those can be a "wrapper of Xlib").

So to use Nuklear with X11 I'd also need to understand that rather than standard Xlib?

https://tronche.com/gui/x/xlib-tutorial/2nd-program-anatomy.html

No. You can take the backend, paste into main() the example from the readme and you're basically done (plus some plumbing which C requires - but other languages do not, so this doesn't count as "a need to understand X11 or Nuklear or any other involved library").

Also, there's a good tutorial for the Windows window:

https://github.com/Immediate-Mode-UI/Nuklear/wiki/GDI

I'm glad you found what you were looking for!

Why is there no such for X11?

Nuklear doesn't have any funding unfortunately and is developed and maintained by community. So any PRs are very much welcome. Feel free to write such tutorial for X11 or any other of those thousands of existing or potential backends!

dumblob avatar Mar 14 '22 13:03 dumblob

Feel free to write such tutorial for X11 or any other of those thousands of existing or potential backends!

I can do this, but it may take more time than from someone who has already worked with Nuklear.

mavavilj avatar Mar 14 '22 13:03 mavavilj

I can do this, but it may take more time than from someone who has already worked with Nuklear.

Sound like a definition of "learning". And learning is always good!

dumblob avatar Mar 14 '22 13:03 dumblob

Second the code you're referring to is not a wrapper (which is apparent from the source code of nuklear_xlib.h). It's just yet another backend renderer for Nuklear. There are other backend renderers using X11 (which is another proof why neither of those can be a "wrapper of Xlib").

I'm not sure if I understand this distinction you make. I was using "wrapper" to refer to bindings to another library so that one renames things and may even encapsulate things differently. I interpreted this from that file having those nk_ functions.

It's intuitive that the header exposes the original Xlib as well, but I'm left wondering, what additions does it produce.

mavavilj avatar Mar 14 '22 13:03 mavavilj

It'd be perhaps helpful to have a better place to ask for some small questions about the library though. For example, I just got stuck to not understanding what:

#define NK_XLIB_IMPLEMENTATION

is meant for. It's intuitive, but I didn't find an official explanation from here:

https://github.com/Immediate-Mode-UI/Nuklear/blob/3e834293adf09aa65f065570374f76c16db08efa/src/HEADER

However, based on this:

https://github.com/Immediate-Mode-UI/Nuklear/blob/60c52adfafceb478e9fee571a23e453c0c3df062/demo/x11_xft/nuklear_xlib.h#L50

it's probably just some "required" thing.

mavavilj avatar Mar 14 '22 14:03 mavavilj

I was also able to proceed to the "just a window" example by commenting out bits from https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/main.c:

/* nuklear - v1.32.0 - public domain */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_IMPLEMENTATION
#define NK_XLIB_IMPLEMENTATION
#include "../../nuklear.h"
#include "nuklear_xlib.h"

#define DTIME           20
#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT   600

typedef struct XWindow XWindow;
struct XWindow {
    Display *dpy;
    Window root;
    Visual *vis;
    Colormap cmap;
    XWindowAttributes attr;
    XSetWindowAttributes swa;
    Window win;
    int screen;
    XFont *font;
    unsigned int width;
    unsigned int height;
    Atom wm_delete_window;
};

static void
die(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

static long
timestamp(void)
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL) < 0) return 0;
    return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000);
}

static void
sleep_for(long t)
{
    struct timespec req;
    const time_t sec = (int)(t/1000);
    const long ms = t - (sec * 1000);
    req.tv_sec = sec;
    req.tv_nsec = ms * 1000000L;
    while(-1 == nanosleep(&req, &req));
}

/* ===============================================================
 *
 *                          EXAMPLE
 *
 * ===============================================================*/
/* This are some code examples to provide a small overview of what can be
 * done with this library. To try out an example uncomment the defines */
/*#define INCLUDE_ALL */
/*#define INCLUDE_STYLE */
/*#define INCLUDE_CALCULATOR */
/*#define INCLUDE_CANVAS */
/*#define INCLUDE_OVERVIEW */
/*#define INCLUDE_NODE_EDITOR */

#ifdef INCLUDE_ALL
  #define INCLUDE_STYLE
  #define INCLUDE_CALCULATOR
  #define INCLUDE_CANVAS
  #define INCLUDE_OVERVIEW
  #define INCLUDE_NODE_EDITOR
#endif

#ifdef INCLUDE_STYLE
  #include "../style.c"
#endif
#ifdef INCLUDE_CALCULATOR
  #include "../calculator.c"
#endif
#ifdef INCLUDE_CANVAS
  #include "../canvas.c"
#endif
#ifdef INCLUDE_OVERVIEW
  #include "../overview.c"
#endif
#ifdef INCLUDE_NODE_EDITOR
  #include "../node_editor.c"
#endif

/* ===============================================================
 *
 *                          DEMO
 *
 * ===============================================================*/
int
main(void)
{
    long dt;
    long started;
    int running = 1;
    XWindow xw;
    struct nk_context *ctx;

    /* X11 */
    memset(&xw, 0, sizeof xw);
    xw.dpy = XOpenDisplay(NULL);
    if (!xw.dpy) die("Could not open a display; perhaps $DISPLAY is not set?");
    xw.root = DefaultRootWindow(xw.dpy);
    xw.screen = XDefaultScreen(xw.dpy);
    xw.vis = XDefaultVisual(xw.dpy, xw.screen);
    xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vis,AllocNone);

    xw.swa.colormap = xw.cmap;
    xw.swa.event_mask =
        ExposureMask | KeyPressMask | KeyReleaseMask |
        ButtonPress | ButtonReleaseMask| ButtonMotionMask |
        Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask|
        PointerMotionMask | KeymapStateMask;
    xw.win = XCreateWindow(xw.dpy, xw.root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0,
        XDefaultDepth(xw.dpy, xw.screen), InputOutput,
        xw.vis, CWEventMask | CWColormap, &xw.swa);

    XStoreName(xw.dpy, xw.win, "X11");
    XMapWindow(xw.dpy, xw.win);
    xw.wm_delete_window = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(xw.dpy, xw.win, &xw.wm_delete_window, 1);
    XGetWindowAttributes(xw.dpy, xw.win, &xw.attr);
    xw.width = (unsigned int)xw.attr.width;
    xw.height = (unsigned int)xw.attr.height;

    /* GUI */
    xw.font = nk_xfont_create(xw.dpy, "fixed");
    ctx = nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height);

    #ifdef INCLUDE_STYLE
    /*set_style(ctx, THEME_WHITE);*/
    /*set_style(ctx, THEME_RED);*/
    /*set_style(ctx, THEME_BLUE);*/
    /*set_style(ctx, THEME_DARK);*/
    #endif

    while (running)
    {
        /* Input */
        XEvent evt;
        started = timestamp();
        nk_input_begin(ctx);
        while (XPending(xw.dpy)) {
            XNextEvent(xw.dpy, &evt);
            if (evt.type == ClientMessage) goto cleanup;
            if (XFilterEvent(&evt, xw.win)) continue;
            nk_xlib_handle_event(xw.dpy, xw.screen, xw.win, &evt);
        }
        nk_input_end(ctx);

        /* GUI */
        /* if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200),
            NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|
            NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE))
        {
            enum {EASY, HARD};
            static int op = EASY;
            static int property = 20;

            nk_layout_row_static(ctx, 30, 80, 1);
            if (nk_button_label(ctx, "button"))
                fprintf(stdout, "button pressed\n");
            nk_layout_row_dynamic(ctx, 30, 2);
            if (nk_option_label(ctx, "easy", op == EASY)) op = EASY;
            if (nk_option_label(ctx, "hard", op == HARD)) op = HARD;
            nk_layout_row_dynamic(ctx, 25, 1);
            nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1);
        }
        nk_end(ctx);
        if (nk_window_is_hidden(ctx, "Demo")) break; */

        /* -------------- EXAMPLES ---------------- */
        /* #ifdef INCLUDE_CALCULATOR
          calculator(ctx);
        #endif
        #ifdef INCLUDE_CANVAS
          canvas(ctx);
        #endif
        #ifdef INCLUDE_OVERVIEW
          overview(ctx);
        #endif
        #ifdef INCLUDE_NODE_EDITOR
          node_editor(ctx);
        #endif */
        /* ----------------------------------------- */

        /* Draw */
        XClearWindow(xw.dpy, xw.win);
        nk_xlib_render(xw.win, nk_rgb(30,30,30));
        XFlush(xw.dpy);

        /* Timing */
        dt = timestamp() - started;
        if (dt < DTIME)
            sleep_for(DTIME - dt);
    }

cleanup:
    nk_xfont_del(xw.dpy, xw.font);
    nk_xlib_shutdown();
    XUnmapWindow(xw.dpy, xw.win);
    XFreeColormap(xw.dpy, xw.cmap);
    XDestroyWindow(xw.dpy, xw.win);
    XCloseDisplay(xw.dpy);
    return 0;
}

However, if I comment out ctx, then I can still get the window, but it will produce a Segmentation Fault and close. So it seems like having the ctx is somehow essential?

This is a bit similar to how the GDI example seemed to be structured:

https://github.com/Immediate-Mode-UI/Nuklear/wiki/GDI#the-windows-window

It's also possibly implied by the allegro5 example that the ctx might be essential:

https://github.com/Immediate-Mode-UI/Nuklear/blob/a3cfd2a9105cc4a8ece29008acacb261ff7522b4/demo/allegro5/main.c#L136

mavavilj avatar Mar 14 '22 14:03 mavavilj

A wrapper is code implementing an abstraction over an existing API to achieve a better alignment for the given situation or context. Nothing like that can be seen in the backends you have referred to.

ctx is a shortcut for context. You can easily see from nuklear.h source code that it's a struct holding the essential data (and only them) for the library to function. Some see it as one of the approaches how to express OOP-like programming C.

#define NK_XLIB_IMPLEMENTATION is the traditional way to avoid multiple includes of the same thing in C. It's the standard approach single-header libraries use.

Overall I feel you should take a deep dive into the C language itself and then just carefully, several times in a row, read the source code of Nuklear and the backends. And then make your hands dirty without asking anyone about anything - you know "learning by doing" and "learning it the hard way".

I'm already out of my family free time budget to support you on your path to programming in C, so feel free to take any advanced courses on the C language to learn all these patterns, tricks, etc. (plenty of stuff is online - incl. youtube). It's very important for programming in C to understand and practise the details.

Or others not knowing your situation will be reluctant to answer your very basic questions making it feel it all stems from a rather serious lack of knowledge of the programming language trivia and real-world projects written in it.

One last advice from me :wink: - it's always better to say up front something relevant about you if you're a student or generally a "greenhorn" in the given domain or whatever. That sparks interest in others and you'll get easily digestable advice. Asking about basics over and over though doesn't have this welcoming effect - rather the opposite. It also pressures the whole community and members start to feel strained.

What is your position then?

dumblob avatar Mar 14 '22 15:03 dumblob

What is your position then?

Yes I am refreshing C alongside, but I still find that the documentation for Nuklear leaves much to be desired about basic things.

https://github.com/Immediate-Mode-UI/Nuklear/blob/c91ca8b5036557b2802af239138223a11300c396/src/nuklear.h#L313

or

https://immediate-mode-ui.github.io/Nuklear/doc/index.html#nuklear/api/context

does not say that a Context is essential. It suggests it's something important though. It was also already suggested that Nuklear does not need a backend (which suggested that code should work without Nuklear).

However, some Nuklear is found to be essential, since in the above example, one must retain:

struct nk_context *ctx;

and

ctx = nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height);

But it seems that one may comment out

nk_input_begin(ctx); and nk_input_end(ctx);

even when the allegro5 example suggested that one shouldn't.

mavavilj avatar Mar 14 '22 15:03 mavavilj

struct nk_context *ctx; and all the "issues" you described has literally nothing to do with Nuklear. It's pure C. It's C89 - i.e. many tenths years old standard existing many tenths years before Nuklear even begain its existence.

This is really C trivia - it's an on-stack instantiated variable sized so that it can hold a pointer. What you found in nuklear.h is not the instantiated variable but an API which requires you to pass a pointer to the function but it doesn't have anything to do with instantiation of the pointer it receives. In other words the API has nothing to do with your decision to instantiate a variable holding the pointer.

In your case you even decided to remove the variable and then wonder that the code doesn't work properly and even manage to blame Nuklear for that. That's so much far from reality I'll just repeat myself - please go and learn C. You won't learn it from my descriptions here nor from reading nuklear.h. You'll learn it step by step from some very thorough tutorial followed by tens of hours (if you're genius) or hundreds and thousands of hours (if you're mere mortal as I'm) of writing applications, learning C standard APIs, POSIX APIs, WinAPI, etc. Then you'll need to spend at least the same amount of time reading complex SW. And then you'll realize everything I've told you during the last two days :wink:. Please take my word and do your necessary exercise. We'll be looking forward to you coming back here then!

Btw. nk_input_begin(ctx) and nk_input_end(ctx) are absolutely essential else expect everything to catch fire.

dumblob avatar Mar 14 '22 16:03 dumblob

In other words the API has nothing to do with your decision to instantiate a variable holding the pointer.

Yes, so one can call nk_xlib_init without taking note of its pointer https://github.com/Immediate-Mode-UI/Nuklear/blob/60c52adfafceb478e9fee571a23e453c0c3df062/demo/x11/nuklear_xlib.h#L19 but then nk_input_begin and nk_input_end could not be used. I didn't find alternative implementations for them, just these:

https://github.com/Immediate-Mode-UI/Nuklear/blob/6e80e2a646f35be4afc157a932f2936392ec8f74/src/nuklear_input.c#L10

BTW: you don't have to answer to all my requests. I'm just posting these so that others who are troubled by things may find help.

mavavilj avatar Mar 14 '22 16:03 mavavilj

So based on the previous message one can also replace the respective parts in the code with:

nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height); (not saved to a pointer)

nk_input_begin(&xlib.ctx);

and

nk_input_end(&xlib.ctx);

However, this is likely not an advisable way to utilize ctx.

mavavilj avatar Mar 14 '22 16:03 mavavilj

nk_xlib_init() exists to do some X11 stuff and allocate and pre-fill the Nuklear context for you. If you ignore the newly created context, you're probably out of luck as it won't be easily possible to create a compatible context on your own.

nk_input_begin() etc. do require context for good reasons.

But please learn C first. This discussion won't bring you anything.

Unfortunately I strongly doubt that "others who are troubled by things may find help" as these questions are not about Nuklear but about pure C.

dumblob avatar Mar 14 '22 16:03 dumblob

Again the info on this is scattered, since I found:

https://github.com/vurtun/nuklear/blob/master/demo/x11_rawfb/main.c#L182

that related nk_xlib_init() to framebuffer, which I understand from the context of OpenGL (https://stackoverflow.com/a/9759063).

However, here:

https://github.com/Immediate-Mode-UI/Nuklear/blob/60c52adfafceb478e9fee571a23e453c0c3df062/demo/x11/nuklear_xlib.h#L614

it seems like one'd need to know about Xlib. When it was earlier said that Nuklear didn't need a backend (or does it? https://stackoverflow.com/a/59831524).

Compare this to how GLUT documents initialization:

https://www.opengl.org/resources/libraries/glut/spec3/node9.html https://www.opengl.org/resources/libraries/glut/spec3/node10.html

Or GLUI:

https://github.com/libglui/glui/search?q=GLUI_glut_init%28%29

In essence, the code doesn't say what nk_xlib_init() does or whether it must be called, but assumes that the user figures these out.

Unfortunately I strongly doubt that "others who are troubled by things may find help" as these questions are not about Nuklear but about pure C.

Nuklear -> Xlib?

Or, in other words, the question is about what Nuklear does in addition to this kind of "just a window" generic X11 discussed here:

http://mech.math.msu.su/~vvb/2course/Borisenko/CppProjects/GWindow/xintro.html

or here:

https://rosettacode.org/wiki/Window_creation/X11#Xlib

mavavilj avatar Mar 14 '22 16:03 mavavilj

It seems that the "just a window" main.c is sort of like the following:

/* nuklear - v1.32.0 - public domain */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_IMPLEMENTATION
#define NK_XLIB_IMPLEMENTATION
#include "../../nuklear.h"
#include "nuklear_xlib.h"

#define DTIME           20
#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT   600

typedef struct XWindow XWindow;
struct XWindow {
    Display *dpy;
    Window root;
    Visual *vis;
    Colormap cmap;
    XWindowAttributes attr;
    XSetWindowAttributes swa;
    Window win;
    int screen;
    XFont *font;
    unsigned int width;
    unsigned int height;
    Atom wm_delete_window;
};

static void
die(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

static long
timestamp(void)
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL) < 0) return 0;
    return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000);
}

static void
sleep_for(long t)
{
    struct timespec req;
    const time_t sec = (int)(t/1000);
    const long ms = t - (sec * 1000);
    req.tv_sec = sec;
    req.tv_nsec = ms * 1000000L;
    while(-1 == nanosleep(&req, &req));
}

/* ===============================================================
 *
 *                          EXAMPLE
 *
 * ===============================================================*/
/* This are some code examples to provide a small overview of what can be
 * done with this library. To try out an example uncomment the defines */
/*#define INCLUDE_ALL */
/*#define INCLUDE_STYLE */
/*#define INCLUDE_CALCULATOR */
/*#define INCLUDE_CANVAS */
/*#define INCLUDE_OVERVIEW */
/*#define INCLUDE_NODE_EDITOR */

/* #ifdef INCLUDE_ALL
  #define INCLUDE_STYLE
  #define INCLUDE_CALCULATOR
  #define INCLUDE_CANVAS
  #define INCLUDE_OVERVIEW
  #define INCLUDE_NODE_EDITOR
#endif

#ifdef INCLUDE_STYLE
  #include "../style.c"
#endif
#ifdef INCLUDE_CALCULATOR
  #include "../calculator.c"
#endif
#ifdef INCLUDE_CANVAS
  #include "../canvas.c"
#endif
#ifdef INCLUDE_OVERVIEW
  #include "../overview.c"
#endif
#ifdef INCLUDE_NODE_EDITOR
  #include "../node_editor.c"
#endif */

/* ===============================================================
 *
 *                          DEMO
 *
 * ===============================================================*/
int
main(void)
{
    long dt;
    long started;
    int running = 1;
    XWindow xw;
    struct nk_context *ctx;

    /* X11 */
    memset(&xw, 0, sizeof xw);
    xw.dpy = XOpenDisplay(NULL);
    if (!xw.dpy) die("Could not open a display; perhaps $DISPLAY is not set?");
    xw.root = DefaultRootWindow(xw.dpy);
    xw.screen = XDefaultScreen(xw.dpy);
    xw.vis = XDefaultVisual(xw.dpy, xw.screen);
    xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vis,AllocNone);

    xw.swa.colormap = xw.cmap;
    xw.swa.event_mask =
        ExposureMask | KeyPressMask | KeyReleaseMask |
        ButtonPress | ButtonReleaseMask| ButtonMotionMask |
        Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask|
        PointerMotionMask | KeymapStateMask;
    xw.win = XCreateWindow(xw.dpy, xw.root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0,
        XDefaultDepth(xw.dpy, xw.screen), InputOutput,
        xw.vis, CWEventMask | CWColormap, &xw.swa);

    XStoreName(xw.dpy, xw.win, "X11");
    XMapWindow(xw.dpy, xw.win);
    xw.wm_delete_window = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(xw.dpy, xw.win, &xw.wm_delete_window, 1);
    XGetWindowAttributes(xw.dpy, xw.win, &xw.attr);
    xw.width = (unsigned int)xw.attr.width;
    xw.height = (unsigned int)xw.attr.height;

    /* GUI */
    xw.font = nk_xfont_create(xw.dpy, "fixed");
    ctx = nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height);

    #ifdef INCLUDE_STYLE
    /*set_style(ctx, THEME_WHITE);*/
    /*set_style(ctx, THEME_RED);*/
    /*set_style(ctx, THEME_BLUE);*/
    /*set_style(ctx, THEME_DARK);*/
    #endif

    while (running)
    {
        /* Input */
        XEvent evt;
        started = timestamp();
        nk_input_begin(ctx);
        while (XPending(xw.dpy)) {
            XNextEvent(xw.dpy, &evt);
            if (evt.type == ClientMessage) goto cleanup;
            if (XFilterEvent(&evt, xw.win)) continue;
            nk_xlib_handle_event(xw.dpy, xw.screen, xw.win, &evt);
        }
        nk_input_end(ctx);

        /* GUI */
        /* if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200),
            NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|
            NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE))
        {
            enum {EASY, HARD};
            static int op = EASY;
            static int property = 20;

            nk_layout_row_static(ctx, 30, 80, 1);
            if (nk_button_label(ctx, "button"))
                fprintf(stdout, "button pressed\n");
            nk_layout_row_dynamic(ctx, 30, 2);
            if (nk_option_label(ctx, "easy", op == EASY)) op = EASY;
            if (nk_option_label(ctx, "hard", op == HARD)) op = HARD;
            nk_layout_row_dynamic(ctx, 25, 1);
            nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1);
        }
        nk_end(ctx);
        if (nk_window_is_hidden(ctx, "Demo")) break; */

        /* -------------- EXAMPLES ---------------- */
        /* #ifdef INCLUDE_CALCULATOR
          calculator(ctx);
        #endif
        #ifdef INCLUDE_CANVAS
          canvas(ctx);
        #endif
        #ifdef INCLUDE_OVERVIEW
          overview(ctx);
        #endif
        #ifdef INCLUDE_NODE_EDITOR
          node_editor(ctx);
        #endif */
        /* ----------------------------------------- */

        /* Draw */
        XClearWindow(xw.dpy, xw.win);
        nk_xlib_render(xw.win, nk_rgb(30,30,30));
        XFlush(xw.dpy);

        /* Timing */
        dt = timestamp() - started;
        if (dt < DTIME)
            sleep_for(DTIME - dt);
    }

cleanup:
    nk_xfont_del(xw.dpy, xw.font);
    nk_xlib_shutdown();
    XUnmapWindow(xw.dpy, xw.win);
    XFreeColormap(xw.dpy, xw.cmap);
    XDestroyWindow(xw.dpy, xw.win);
    XCloseDisplay(xw.dpy);
    return 0;
}

mavavilj avatar Mar 14 '22 17:03 mavavilj

You're comparing backends like apples and oranges, same goes for other things. I will not comment on that as it leads nowhere.

it seems like one'd need to know about Xlib. When it was earlier said that Nuklear didn't need a backend (or does it? https://stackoverflow.com/a/59831524).

No, Nuklear doesn't need any backend - it outputs a list of "instructions/commands" into a given array in memory. Nothing more nothing less. Backends than take this list of instructions and try to interpret it e.g. by drawing according to the instructions/commands. But you can freely just ignore the list and you can compile such Nuklear app and run it. It will work (but of course output nothing because you'll be ignoring the computed list of instructions).

Compare this to how GLUT documents initialization:

https://www.opengl.org/resources/libraries/glut/spec3/node9.html https://www.opengl.org/resources/libraries/glut/spec3/node10.html

Not comparable - GLUT is not a UI library (it doesn't do any layouting, nor widgeting nor font handling nor anything closely related).

In essence, the code doesn't say what nk_xlib_init() does or whether it must be called, but assumes that the user figures these out.

All demos and examples have the same goal - to save you from figuring out what it does. So I don't buy what you're writing and actually have no idea how you came up with that :open_mouth:.

Unfortunately I strongly doubt that "others who are troubled by things may find help" as these questions are not about Nuklear but about pure C.

Nuklear -> Xlib?

No, this thread is really solely about C. Therefore I meant Nuklear and not Xlib. I can't find here anything which you'd ask about if you knew C good enough.

Or, in other words, the question is about what Nuklear does in addition to this kind of "just a window" generic X11 discussed here: http://mech.math.msu.su/~vvb/2course/Borisenko/CppProjects/GWindow/xintro.html

or here:

https://rosettacode.org/wiki/Window_creation/X11#Xlib

All the UI - especially layouting (positioning, layering, sizing, ...), defining widgets and their composition, everything font-related, image & other formats (e.g. icons) compatibility, nice immediate mode API, generic input handling, generic output handling. To name a few.

dumblob avatar Mar 14 '22 17:03 dumblob

Do you really say that the following is not wrapping?

https://github.com/Immediate-Mode-UI/Nuklear/blob/60c52adfafceb478e9fee571a23e453c0c3df062/demo/x11/nuklear_xlib.h#L527

A wrapper function is a subroutine (another word for a function) in a software library or a computer program whose main purpose is to call a second subroutine or a system call with little or no additional computation.

mavavilj avatar Mar 14 '22 18:03 mavavilj

No, it's not a wrapper as the full signature is NK_INTERN void nk_xsurf_clear(XSurface *surf, unsigned long color) and NK_INTERN means the function shall be used only internally (i.e. it's an abstraction no different from any other usual abstraction to structure code to increase understandability and maintainability) and not as wrapper (a wrapper is architectured so that the wrapper's API is meant to be used by external code - not just the internal code).

dumblob avatar Mar 14 '22 19:03 dumblob

This discussion got a bit off what was originally intended. All the /demo examples provide something like the following as a basic example...

/* GUI */
if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250),
    NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|
    NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE))
{
    enum {EASY, HARD};
    static int op = EASY;
    static int property = 20;

    nk_layout_row_static(ctx, 30, 80, 1);
    if (nk_button_label(ctx, "button"))
        printf("button pressed!\n");
    nk_layout_row_dynamic(ctx, 30, 2);
    if (nk_option_label(ctx, "easy", op == EASY)) op = EASY;
    if (nk_option_label(ctx, "hard", op == HARD)) op = HARD;
    nk_layout_row_dynamic(ctx, 22, 1);
    nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1);

    nk_layout_row_dynamic(ctx, 20, 1);
    nk_label(ctx, "background:", NK_TEXT_LEFT);
    nk_layout_row_dynamic(ctx, 25, 1);
    if (nk_combo_begin_color(ctx, nk_rgb_cf(bg), nk_vec2(nk_widget_width(ctx),400))) {
        nk_layout_row_dynamic(ctx, 120, 1);
        bg = nk_color_picker(ctx, bg, NK_RGBA);
        nk_layout_row_dynamic(ctx, 25, 1);
        bg.r = nk_propertyf(ctx, "#R:", 0, bg.r, 1.0f, 0.01f,0.005f);
        bg.g = nk_propertyf(ctx, "#G:", 0, bg.g, 1.0f, 0.01f,0.005f);
        bg.b = nk_propertyf(ctx, "#B:", 0, bg.b, 1.0f, 0.01f,0.005f);
        bg.a = nk_propertyf(ctx, "#A:", 0, bg.a, 1.0f, 0.01f,0.005f);
        nk_combo_end(ctx);
    }
}
nk_end(ctx);

RobLoach avatar Mar 16 '24 23:03 RobLoach

See the docs too! Nice work, all https://github.com/Immediate-Mode-UI/Nuklear/wiki/Window

RobLoach avatar Apr 04 '24 18:04 RobLoach