GUI Problems...

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Warmaster199

GUI Problems...

Post by Warmaster199 »

You may remember my GUI program from several months ago. I posted it here and requested optimizations because I thought that it ran slow. I have took your recommendations and implemented linked lists and binary trees in my GUI. It is completely recoded and includes linked lists, binary trees, double buffers, bitmaps, buttons, and just today, I added event handler support code. When you press a button, an event handler (if exists) is called with special flags. the even handler is what the user makes and passes when you create a control. Very nice, highly customizable (compared to "home made" GUIs that I have seen). My GUI's performance is phenominal(I think so!!!). On my P200, I can get 70 Frames per second!!! (70 screen updates per second).

But now, I have a problem: In resolution 320x200x256: I have a main window with a titlebar and window caption, as well as 2 buttons and a bar control that I made as a separator (like a menu separator - no useful purpose). Then I have a desktop window (With a bitmap loaded from a file - my CTOS stuff), and a plain window with caption and titlebar. For the first window, button1 creates a new window with a close button on each(everytime it is pressed creates a new - the buttons are linked to an event handler function to close the window), button2 closes this(the main) window and frees all it's controls and bitmaps... EVERYTHING that should be freed. and hbar1 (the menu bar) clears the screen and displays the text "hb1 pressed!".

If you press the first button 3 times (create 3 windows), it is fine: Press it once more, and I get an "error while allocating control bitmap" meaning that I cannot allocate a bitmap for a control on a window that is allocated and that means that the whole GUI must die, or it will mess up and take the whole PC with it (I tried ditching that code and that's what happened). The only thing that I can understand about this error is that I somehow have ran out of RAM, so malloc() can't grab anymore memory. >:(

BUT: If I make the new windows without close buttons(via code comments), then I have had about 30 windows created with titlebars(no buttons) with not even a slight speed decrease noticable :) (I removed my FPS code because it crashes the Celeron PCs at my school ??? - all other PCs are fine (P200, P3@500, [email protected])). I don't know, but I think that the memory allocation error is either in addbutton(winhndl, ctrlhndl, buttonstruct, eventproc(message)); or in addcontrol(winhndl, ctrlhndl, crect, cflags, void controlstruct, eventproc(message)); I cannot see the error being anywhere else because everything else works flawlessly! I MAY be allocating the bitmaps as a wrong size, but then during redraws, I would get black parts on the screen (draw overs). Tomarrow, I may post my executable and the bitmap that's needed to run(desktop bitmap) it (Please don't ask for code :( )

Please help me figure out where to start looking... The program is over 2000 lines... I compile in LARGE memory model using BorlandC 3.1 (It's a totally awesome DOS IDE). The compiled executable is ~52KBytes, code is about 54KBytes...
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GUI Problems...

Post by Pype.Clicker »

i might give it a look, but i hardly see how i could help you tracking the bug if no source is provided (not meaning that you should post them)... what kind of help do expect ?
DarylD

Re:GUI Problems...

Post by DarylD »

I have to agree with Pype, without code its almost impossible for us to suggest anything. There is too many unknowns.

Daryl.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GUI Problems...

Post by Pype.Clicker »

here are a few clues a can give you:
1. try replacing your simple malloc() by some personnal code that will count how much allocations and free() have been made. If you store that information in a small size-indexed table, you'll quickly find out what kind of memory item is leaking.

Code: Select all

MallocMonitor extends malloc {
     int allocs[32];

     int countbits(int val) {
        int i;
        for (i=0; val; i++,val=val/2);
        return i;
     }

     Mem malloc(int size) {
         Mem ptr;
         if (ptr=super.malloc(size)) allocs[countbits(size)]++;
         return ptr;
     }
 
     void free(Mem m) {
          allocs[countbits(Mem.blocksize)]--;
          super.free(m);
     }

     String report() {
          String str="memory report for malloc():\n";
          for (i=0;i<allocs.size;i++) 
               str+="["+(1<<i)+"] : "+allocs[i]+"\n";
     }
}
Warmaster199

Re:GUI Problems...

Post by Warmaster199 »

The source related to the error causing code:

Code: Select all

/* Everytime a user clicks a close box on a window, they will
*  invoke this function */
int wincloseproc(GUIMSGPKT *messages)
{
    struct WINDLIST *wnd;

    /* Window message */
    if(messages->parent_type == 1)
    {
   if (messages->parent_struct != NULL)
   {
       wnd = (struct WINDLIST *) messages->parent_struct;

       /* For now, the 'close parent' command is 1 */
       if (messages->cmd_type == 1)
       {
      destroywin(wnd->handle);
       }
   }
    }
}

CONTROL_HANDLE addcontrol(WINDOW_HANDLE whandle, CONTROL_HANDLE chandle, RECT *rect, unsigned long *flags, long ctrl_type, void *ctrl_struct, void (*repaint_func)(void *control_container_struct), int (*event_handler)(GUIMSGPKT *messages))
{
    struct WINDLIST *wnd;
    struct CONTROLLIST *ctrl;
    CONTROL_HANDLE ctrl_hndl;

    if (whandle >= wm_num_windows) return 0;
    wnd = wm_handles[whandle];
    if (wnd == NULL) return 0;

    /* Allocate a control to the window (or parent control) then check
    *  to make sure it properly allocated. Return 0 if not */


    ctrl = malloc(sizeof(struct CONTROLLIST));
    if (ctrl == NULL) return 0;      /* Unable to allocate... */

    wnd->cm_handles = realloc(wnd->cm_handles, sizeof(struct CONTROLLIST *) * (wnd->cm_num_controls + 1));
    wnd->cm_handles[wnd->cm_num_controls] = ctrl;

    ctrl->prev = ctrl->next = NULL;
    ctrl->first_child = wnd->last_child = NULL;
    ctrl->parent = wnd->cm_handles[chandle];

    ctrl->handle = wnd->cm_num_controls++;
    ctrl_hndl = ctrl->handle;
    ctrl->win = wnd;

    /* So far so good, setup the control */
    ctrl->type = ctrl_type;

    /* Setup and allocate the control's bitmap */
    ctrl->cbmp.width = rect->x2 - rect->x1;
    ctrl->cbmp.height = rect->y2 - rect->y1;

    if ((ctrl->cbmp.data = (unsigned char *) malloc((rect->x2 - rect->x1 * rect->y2 - rect->y1))) == NULL)
    {
   set_mode(0x03);

   printf("Error allocating memory for control bitmap region.\n");
   getch();
   exit(1);
    }

    memset(ctrl->cbmp.data, 0, (rect->x2 - rect->x1) * (rect->y2 - rect->y1));

    /* Transfer structure information over to the container */
    ctrl->control_container = ctrl_struct;
    ctrl->repaint_func = repaint_func;
    ctrl->event_handler = event_handler;
    ctrl->rect = rect;
    ctrl->flags = flags;
    ctrl->needs_repaint = 1;

    focuscontrol(whandle, ctrl_hndl);

    /* Success, return the handle */
    return ctrl_hndl;
}

CONTROL_HANDLE addbutton (WINDOW_HANDLE whandle, CONTROL_HANDLE chandle, struct ctrl_button *button, int (event_handler)(GUIMSGPKT *messages))
{
    struct WINDLIST *wnd;
    struct CONTROLLIST *ctrl;
    CONTROL_HANDLE ctrl_hndl;

    if (whandle >= wm_num_windows) return 0;
    wnd = wm_handles[whandle];
    if (wnd == NULL) return 0;

    if (chandle >= wnd->cm_num_controls)
   return 0;

    ctrl = wnd->cm_handles[chandle];
    if (ctrl == NULL)
   return 0;

    ctrl_hndl = addcontrol(whandle, chandle, &button->position, &button->flags, CTRL_BUTTON, (struct ctrl_button *)button, &repaint_button, event_handler);
    if (ctrl_hndl == 0)
    {
   set_mode(0x03);
   printf ("Control failure: Couldn't allocate");
   getch();
   exit(1);
    }

    button->member_of = wnd->cm_handles[ctrl_hndl];

    return ctrl_hndl;
}
The code to create each new window:

Code: Select all

          tw = createwin(0, 20, 20, 40, 30, "New", WMFLAGS_DEFAULT, NULL);
          btemp.position.x1 = wm_handles[tw]->position.x2 - wm_handles[tw]->position.x1 - 13;
          btemp.position.y1 = 3;
          btemp.position.x2 = btemp.position.x1 + 10;
          btemp.position.y2 = 13;
          btemp.flags = 0x01;
          addbutton(tw, 0, &btemp, wincloseproc);
Instructions: Use arrow keys to move cursor. Clicks are actually toggles: Press space bar to grab something, and then spacebar again to release it. You can move titlebars, resize windows by clicking the little round knobby in the bottom right corner of default windows.

Setup: You MUST put 1stdesk.bmp into C:\ (as in the root of the drive). BUT: You may run the EXE from anywhere(Even inside the archive).
Warmaster199

Re:GUI Problems...

Post by Warmaster199 »

MORE CODE THAT WOULDN'T FIT IN PREV. MESSAGE:

Code: Select all

typedef struct tagBITMAP              /* the structure for a bitmap. */
{
    unsigned int width;
    unsigned int height;
    unsigned char *data;
} BITMAP;

typedef struct ctrl_button
{
    RECT position;         /* Rect relative(inside) window */
    char text[256];
    unsigned long flags;      /* Flags are mainly for the repaint/draw functions */

    struct CONTROLLIST *member_of;   /* 'Parent" of this function */
};

struct CONTROLLIST
{
    unsigned int handle;      /* Control ID */
    unsigned long type;       /* Button? Label? Textbox? This tells you! */
    unsigned long *flags;      /* Pointer to control_container's flags */
    unsigned char needs_repaint;

    RECT *rect;

    void *control_container;  /* Pointer to a control struct like a button */
    void (*repaint_func)(void *control_pointer);
    int (*event_handler)(GUIMSGPKT *messages);

    BITMAP cbmp;

    /* This is a tree structure so that we can have controls that are part of
    *  other controls... (Textboxes with scrollbars for example) */
    struct CONTROLLIST *prev, *next;
    struct CONTROLLIST *first_child, *last_child;
    struct CONTROLLIST *parent;

    struct WINDLIST *win;     /* I belong to whom? */
};
Please email me if there are more questions...
Tim

Re:GUI Problems...

Post by Tim »

It looks like you're allocating a bitmap for each control's image. This uses a lot of memory unnecessarily; it is frowned upon in 'big' GUIs (e.g. Windows) and far too wasteful in a real mode program like this.

Instead of drawing to a memory bitmap and blitting that to the screen, redraw the control directly to the screen each time it needs painting.
Warmaster199

Re:GUI Problems...

Post by Warmaster199 »

Plotting directly to the screen seems skippish to me. True drawing to bitmaps wastes a bit of memory, but not much: For the little 10x10 closebox buttons, only 100bytes is allocated. I have used the windows system monitor's memory allocation check and confirmed that windows DOES use this method. I assume other GUIs(shells) like KDE and Gnome do the same thing... If you want the user to be pleased with smooth graphics, it needs to be plotted to a bitmap. If there is a better way, then please do say. Otherwise, I will not be able to get this working and then I WILL have to plot directly to the screen. Also note that this is just the GUI test program to make sure everything works(Yes, it's real mode). The real GUI will be housed inside a protected mode kernel (once it is working and has a memory manager and keyboard driver - I haven't worked on the kernel in quite a while).

I also tried making myself a blit() function about a month ago, but it kept crashing, so I gave up on it... it may help later. Two more things that I need in that GUI: To implement a malloc extender to detect leaks (like Pype said ;) ), as well as making a shutdowngui(); function that will free all windows, handles, and controls:
Notice this:

Code: Select all

    set_mode(0x03);
    printf("Error allocating memory for control bitmap region.\n");
    getch();
    exit(1);
should be this:

Code: Select all

    shutdowngui();
    set_mode(0x03);
    printf("Error allocating memory for control bitmap region.\n");
    getch();
    exit(1);
Also, does anyone know of a simple PMode init and deinit stub that will work with BorlandC?
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GUI Problems...

Post by Pype.Clicker »

Warmaster199 wrote: Plotting directly to the screen seems skippish to me. True drawing to bitmaps wastes a bit of memory, but not much: For the little 10x10 closebox buttons, only 100bytes is allocated. I have used the windows system monitor's memory allocation check and confirmed that windows DOES use this method. I assume other GUIs(shells) like KDE and Gnome do the same thing...
Okay for the 10x10 button, but what about a 300x400 window holding some text you typed ? will you keep a bitmap for it ??

Under linux, a few memory manager do some "caching" of the bitmap they send (this is usually known as "saveunder"), but it mainly happens when the window get obscured by another one. As long as it is visible, its data are on the video RAM, so why bothering with a full bitmap copy of it ??

You find it's not a waste because your system is still quite limitted and works in 320x200x256 (a max of 64KB :). But it surely won't support 1600x1200xtruecolor (7MB per screen) with an average of 2 shadowed window per pixel (like messy people are used to create)

Also, does anyone know of a simple PMode init and deinit stub that will work with BorlandC?
You can't find one because BorlandC assumes 16 bits programming and DOS environment :-?
You'll have to turn to ANSI C and use DjGPP or something similar...
DarylD

Re:GUI Problems...

Post by DarylD »

Ahh, memories. I remember doing my first OS about 14 years ago in mode 0x13. I only had a boot sector and a second stage loader but seeing my splash screen made me very happy!!

I agree with Pype, saving the contents of each window as a bitmap will certainly make it run smoother and faster but it will gobble memory at a frightening rate. One thing that is good to do is to store icons/bitmaps etc in spare video memory (on cards that support it with the right drivers) because that will just require a video-video memory blit for your close button for example.

But saying that, writing drivers like that is a lengthy complicated task.

BTW, IIRC, Mac OS X stores all windows as bitmaps for speed because of its rendering engine being so CPU heavy recreating window contents. That could be why it requires 512Mb for a reasonable experience.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:GUI Problems...

Post by Pype.Clicker »

DarylD wrote: BTW, IIRC, Mac OS X stores all windows as bitmaps for speed because of its rendering engine being so CPU heavy recreating window contents. That could be why it requires 512Mb for a reasonable experience.
I believe the pre-rendered bitmaps should be made as reusable as possible. For instance, font rendering could be made into an intermediate so that when the same font is requested again, you just do a gfxAreaCopyWithAlpha(window,font.cache('z')); rather than re-rastering 'z' for the 3rd time.

If any "saveunder" buffer is used, a memory reclaiming scheme should be provided so that you can control how much memory is used for GUI caching and give priority to windows that have highest refreshing delays (i.e. apps running through the network)
Warmaster199

Re:GUI Problems...

Post by Warmaster199 »

I have fixed my GUI's error. It was in the code that I posted here:
ctrl->first_child = wnd->last_child = NULL;
should have been:
ctrl->first_child = ctrl->last_child = NULL;
and:
if ((ctrl->cbmp.data = (unsigned char *) malloc((rect->x2 - rect->x1 * rect->y2 - rect->y1))) == NULL)
gets turned into:
if ((ctrl->cbmp.data = (unsigned char *) malloc(((rect->x2 - rect->x1) * (rect->y2 - rect->y1)))) == NULL)

Another note, yesterday, a friend of mine brought into school a whole bunch of broken/old computer stuff from his basement. I found an old 386 laptop in the junkpile and he said I could keep it. So now, I have a laptop with 386SX-16MHz, 1MByte RAM, 21MByte HDD, and monochrome LCD, and dead battery. So, I plug in my adapter to run my "new" 13 year old laptop. It has Win3.1. I tried running my GUI on the laptop and it run fairly quickly... Noticably faster than Win3.1 ran, and my GUI uses whole window dragging, not fram dragging. The laptop will not run BorlandC (not enough RAM)
Curufir

Re:GUI Problems...

Post by Curufir »

Hmm. When I started thinking of new and interesting ways to do this I first came up with a variant of the painter's algorithm, which I thought was great until someone who actually knew something about graphics pointed out just how bad it really was ;D.

The graphics server isn't really something I'm looking at right now (There's just so much to play around with before that), but I couldn't stop myself thinking about it while I do other things.

The idea I'm working on is storing the entire screen in video memory and then using a kind of dynamic linked list scheme to represent a log of changes/frame to that screen. So the full bitmap of a window is never actually represented anywhere but video memory, and updates involve locking the list, iterating through it to write all changes, then clear it and unlock. I'm not sure if this will be fast enough (Until such times as I code it) and of course there's a lot more going on in the background to handle all the normal gui operations, but it feels as though it should work ok.
Post Reply