diff options
author | inmarket <andrewh@inmarket.com.au> | 2014-08-12 16:45:06 +1000 |
---|---|---|
committer | inmarket <andrewh@inmarket.com.au> | 2014-08-12 16:45:06 +1000 |
commit | 5460a923ab25d27e522fe175563633665c477e02 (patch) | |
tree | e43734965f66092d3d076a599b3b8a188b005bc0 /src | |
parent | 0e74c164c3eac14f6e99d1a5cc4e0563faeff5d0 (diff) | |
parent | 10902154aec652a3fcdf028b2c6ff16743464973 (diff) | |
download | uGFX-5460a923ab25d27e522fe175563633665c477e02.tar.gz uGFX-5460a923ab25d27e522fe175563633665c477e02.tar.bz2 uGFX-5460a923ab25d27e522fe175563633665c477e02.zip |
Merge branch 'master' into newmouse
Diffstat (limited to 'src')
-rw-r--r-- | src/gdisp/gdisp.c | 6 | ||||
-rw-r--r-- | src/gdisp/image_native.c | 2 | ||||
-rw-r--r-- | src/gevent/gevent.c | 98 | ||||
-rw-r--r-- | src/gevent/sys_defs.h | 21 | ||||
-rw-r--r-- | src/gfile/gfile.c | 788 | ||||
-rw-r--r-- | src/gfile/inc_chibiosfs.c | 33 | ||||
-rw-r--r-- | src/gfile/inc_fatfs.c | 81 | ||||
-rw-r--r-- | src/gfile/inc_memfs.c | 21 | ||||
-rw-r--r-- | src/gfile/inc_nativefs.c | 116 | ||||
-rw-r--r-- | src/gfile/inc_printg.c | 261 | ||||
-rw-r--r-- | src/gfile/inc_romfs.c | 61 | ||||
-rw-r--r-- | src/gfile/inc_scang.c | 257 | ||||
-rw-r--r-- | src/gfile/inc_stdio.c | 45 | ||||
-rw-r--r-- | src/gfile/inc_strings.c | 69 | ||||
-rw-r--r-- | src/gfile/sys_defs.h | 136 | ||||
-rw-r--r-- | src/gfile/sys_options.h | 14 | ||||
-rw-r--r-- | src/gos/gfx_chibios.c (renamed from src/gos/chibios.c) | 27 | ||||
-rw-r--r-- | src/gos/gfx_chibios.h (renamed from src/gos/chibios.h) | 0 | ||||
-rw-r--r-- | src/gos/gfx_ecos.c (renamed from src/gos/ecos.c) | 7 | ||||
-rw-r--r-- | src/gos/gfx_ecos.h (renamed from src/gos/ecos.h) | 0 | ||||
-rw-r--r-- | src/gos/gfx_freertos.c (renamed from src/gos/freertos.c) | 8 | ||||
-rw-r--r-- | src/gos/gfx_freertos.h (renamed from src/gos/freertos.h) | 12 | ||||
-rw-r--r-- | src/gos/gfx_linux.c (renamed from src/gos/linux.c) | 1 | ||||
-rw-r--r-- | src/gos/gfx_linux.h (renamed from src/gos/linux.h) | 0 | ||||
-rw-r--r-- | src/gos/gfx_osx.c (renamed from src/gos/osx.c) | 1 | ||||
-rw-r--r-- | src/gos/gfx_osx.h (renamed from src/gos/osx.h) | 0 | ||||
-rw-r--r-- | src/gos/gfx_raw32.c (renamed from src/gos/raw32.c) | 6 | ||||
-rw-r--r-- | src/gos/gfx_raw32.h (renamed from src/gos/raw32.h) | 22 | ||||
-rw-r--r-- | src/gos/gfx_rawrtos.c | 83 | ||||
-rw-r--r-- | src/gos/gfx_rawrtos.h | 77 | ||||
-rw-r--r-- | src/gos/gfx_win32.c (renamed from src/gos/win32.c) | 2 | ||||
-rw-r--r-- | src/gos/gfx_win32.h (renamed from src/gos/win32.h) | 0 | ||||
-rw-r--r-- | src/gos/sys_defs.h | 16 | ||||
-rw-r--r-- | src/gos/sys_make.mk | 15 | ||||
-rw-r--r-- | src/gos/sys_options.h | 14 | ||||
-rw-r--r-- | src/gos/sys_rules.h | 4 | ||||
-rw-r--r-- | src/gwin/button.c | 3 | ||||
-rw-r--r-- | src/gwin/button.h | 3 | ||||
-rw-r--r-- | src/gwin/checkbox.c | 3 | ||||
-rw-r--r-- | src/gwin/checkbox.h | 3 | ||||
-rw-r--r-- | src/gwin/class_gwin.h | 4 | ||||
-rw-r--r-- | src/gwin/frame.c | 6 | ||||
-rw-r--r-- | src/gwin/gcontainer.c | 13 | ||||
-rw-r--r-- | src/gwin/gcontainer.h | 1 | ||||
-rw-r--r-- | src/gwin/gwidget.c | 57 | ||||
-rw-r--r-- | src/gwin/gwidget.h | 45 | ||||
-rw-r--r-- | src/gwin/list.c | 3 | ||||
-rw-r--r-- | src/gwin/list.h | 3 | ||||
-rw-r--r-- | src/gwin/radio.c | 3 | ||||
-rw-r--r-- | src/gwin/radio.h | 3 | ||||
-rw-r--r-- | src/gwin/slider.c | 3 | ||||
-rw-r--r-- | src/gwin/slider.h | 3 | ||||
-rw-r--r-- | src/gwin/sys_options.h | 9 |
53 files changed, 1653 insertions, 816 deletions
diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index e9ede8ab..b8b4a847 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -21,7 +21,11 @@ #if 1 #undef INLINE - #define INLINE inline + #if defined(__KEIL__) || defined(__C51__) + #define INLINE __inline + #else + #define INLINE inline + #endif #else #undef INLINE #define INLINE diff --git a/src/gdisp/image_native.c b/src/gdisp/image_native.c index c458531e..81344642 100644 --- a/src/gdisp/image_native.c +++ b/src/gdisp/image_native.c @@ -90,7 +90,7 @@ gdispImageError gdispImageCache_NATIVE(gdispImage *img) { return GDISP_IMAGE_ERR_OK; } -gdispImageError gdispImageGDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { +gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { coord_t mx, mcx; size_t pos, len; diff --git a/src/gevent/gevent.c b/src/gevent/gevent.c index 779f63a0..f1dab064 100644 --- a/src/gevent/gevent.c +++ b/src/gevent/gevent.c @@ -22,26 +22,40 @@ #define GEVENT_ASSERT(x) #endif +/* Flags in the listener structure */ +#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy +#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal +#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event + /* This mutex protects access to our tables */ static gfxMutex geventMutex; /* Our table of listener/source pairs */ static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS]; +/* Send an exit event if possible. */ +/* We already have the geventMutex */ +static void doExitEvent(GListener *pl) { + // Don't do the exit if someone else currently has the event lock + if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) { + pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use + pl->event.type = GEVENT_EXIT; // Set up the EXIT event + pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data) + gfxSemSignal(&pl->waitqueue); + } +} + /* Loop through the assignment table deleting this listener/source pair. */ /* Null is treated as a wildcard. */ +/* We already have the geventMutex */ static void deleteAssignments(GListener *pl, GSourceHandle gsh) { GSourceListener *psl; for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) { - if (gfxSemCounter(&psl->pListener->waitqueue) < 0) { - gfxSemWait(&psl->pListener->eventlock, TIME_INFINITE); // Obtain the buffer lock - psl->pListener->event.type = GEVENT_EXIT; // Set up the EXIT event - gfxSemSignal(&psl->pListener->waitqueue); // Wake up the listener - gfxSemSignal(&psl->pListener->eventlock); // Release the buffer lock - } + doExitEvent(psl->pListener); psl->pListener = 0; + psl->pSource = 0; } } } @@ -58,9 +72,9 @@ void _geventDeinit(void) void geventListenerInit(GListener *pl) { gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block - gfxSemInit(&pl->eventlock, 1, 1); // Only one thread at a time looking at the event buffer pl->callback = 0; // No callback active pl->event.type = GEVENT_NULL; // Always safety + pl->flags = 0; } bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) { @@ -80,9 +94,7 @@ bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) { if (pl == psl->pListener && gsh == psl->pSource) { // Just update the flags - gfxSemWait(&pl->eventlock, TIME_INFINITE); // Safety first - just in case a source is using it psl->listenflags = flags; - gfxSemSignal(&pl->eventlock); // Release this lock gfxMutexExit(&geventMutex); return TRUE; } @@ -106,33 +118,37 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh) { if (pl) { gfxMutexEnter(&geventMutex); deleteAssignments(pl, gsh); - if (!gsh && gfxSemCounter(&pl->waitqueue) < 0) { - gfxSemWait(&pl->eventlock, TIME_INFINITE); // Obtain the buffer lock - pl->event.type = GEVENT_EXIT; // Set up the EXIT event - gfxSemSignal(&pl->waitqueue); // Wake up the listener - gfxSemSignal(&pl->eventlock); // Release the buffer lock - } + if (!gsh) + doExitEvent(pl); gfxMutexExit(&geventMutex); } } GEvent *geventEventWait(GListener *pl, delaytime_t timeout) { - if (pl->callback || gfxSemCounter(&pl->waitqueue) < 0) + gfxMutexEnter(&geventMutex); + // Don't allow waiting if we are on callbacks or if there is already a thread waiting + if (pl->callback || (pl->flags & GLISTENER_WAITING)) { + gfxMutexExit(&geventMutex); return 0; + } + pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy + pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread + gfxMutexExit(&geventMutex); return gfxSemWait(&pl->waitqueue, timeout) ? &pl->event : 0; } +void geventEventComplete(GListener *pl) { + pl->flags &= ~GLISTENER_EVENTBUSY; +} + void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) { if (pl) { gfxMutexEnter(&geventMutex); - gfxSemWait(&pl->eventlock, TIME_INFINITE); // Obtain the buffer lock - pl->param = param; // Set the param - pl->callback = fn; // Set the callback function - if (gfxSemCounter(&pl->waitqueue) < 0) { - pl->event.type = GEVENT_EXIT; // Set up the EXIT event - gfxSemSignal(&pl->waitqueue); // Wake up the listener - } - gfxSemSignal(&pl->eventlock); // Release the buffer lock + doExitEvent(pl); + pl->param = param; // Set the param + pl->callback = fn; // Set the callback function + if (fn) + pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available gfxMutexExit(&geventMutex); } } @@ -146,14 +162,13 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las gfxMutexEnter(&geventMutex); - // Unlock the last listener event buffer - if (lastlr) - gfxSemSignal(&lastlr->pListener->eventlock); + // Unlock the last listener event buffer if it wasn't used. + if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE)) + lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); // Loop through the table looking for attachments to this source for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) { if (gsh == psl->pSource) { - gfxSemWait(&psl->pListener->eventlock, TIME_INFINITE); // Obtain a lock on the listener event buffer gfxMutexExit(&geventMutex); return psl; } @@ -163,21 +178,38 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las } GEvent *geventGetEventBuffer(GSourceListener *psl) { - // We already know we have the event lock - return &psl->pListener->callback || gfxSemCounter(&psl->pListener->waitqueue) < 0 ? &psl->pListener->event : 0; + gfxMutexEnter(&geventMutex); + if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) { + // Oops - event buffer is still in use + gfxMutexExit(&geventMutex); + return 0; + } + + // Allocate the event buffer + psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); + gfxMutexExit(&geventMutex); + return &psl->pListener->event; } void geventSendEvent(GSourceListener *psl) { gfxMutexEnter(&geventMutex); - if (psl->pListener->callback) { // This test needs to be taken inside the mutex + if (psl->pListener->callback) { + + // Mark it back as free and as sent. This is early to be marking as free but it protects + // if the callback alters the listener in any way + psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); gfxMutexExit(&geventMutex); - // We already know we have the event lock + + // Do the callback psl->pListener->callback(psl->pListener->param, &psl->pListener->event); } else { // Wake up the listener - if (gfxSemCounter(&psl->pListener->waitqueue) <= 0) + if ((psl->pListener->flags & GLISTENER_WAITING)) { + psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_WAITING); gfxSemSignal(&psl->pListener->waitqueue); + // The listener thread will free the event buffer when ready + } gfxMutexExit(&geventMutex); } } diff --git a/src/gevent/sys_defs.h b/src/gevent/sys_defs.h index c50dc5ae..9f1f4dde 100644 --- a/src/gevent/sys_defs.h +++ b/src/gevent/sys_defs.h @@ -56,7 +56,7 @@ typedef void (*GEventCallbackFn)(void *param, GEvent *pe); // The Listener Object typedef struct GListener { gfxSem waitqueue; // Private: Semaphore for the listener to wait on. - gfxSem eventlock; // Private: Protect against more than one sources trying to use this event lock at the same time + uint16_t flags; // Private: Flags for operation GEventCallbackFn callback; // Private: Call back Function void *param; // Private: Parameter for the callback function. GEvent event; // Public: The event object into which the event information is stored. @@ -149,9 +149,11 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh); * timeout specifies the time to wait in system ticks. * TIME_INFINITE means no timeout - wait forever for an event. * TIME_IMMEDIATE means return immediately - * @note The GEvent buffer is staticly allocated within the GListener so the event does not - * need to be dynamicly freed however it will get overwritten by the next call to - * this routine. + * @note The returned GEvent is released when this routine is called again + * or when optionally @p geventEventComplete() is called. Calling @p geventEventComplete() + * allows the GEvent object to be reused earlier which can reduce missed events. The GEvent + * object MUST NOT be used after this function is called (and is blocked waiting for the next + * event) or after geventEventComplete() is called. * * @param[in] pl The listener * @param[in] timeout The timeout @@ -160,6 +162,17 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh); */ GEvent *geventEventWait(GListener *pl, delaytime_t timeout); +/** + * @brief Release the GEvent buffer associated with a listener. + * @details The GEvent returned by @p geventEventWait() is released. + * @note The GEvent pointer returned by @p geventEventWait() is released when @p geventEventWait() + * is called again or when this function is called. The GEvent + * object MUST NOT be used after this function is called. + * + * @param[in] pl The listener + */ +void geventEventComplete(GListener *pl); + /* @brief Register a callback for an event on a listener from an assigned source. * @details The type of the event should be checked (pevent->type) and then pevent should be typecast to the * actual event type if it needs to be processed. diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c index 6aadda09..caf7f22f 100644 --- a/src/gfile/gfile.c +++ b/src/gfile/gfile.c @@ -35,6 +35,11 @@ struct GFILE { long int pos; }; +struct gfileList { + const struct GFILEVMT * vmt; + bool_t dirs; +}; + typedef struct GFILEVMT { const struct GFILEVMT * next; uint8_t flags; @@ -59,6 +64,11 @@ typedef struct GFILEVMT { bool_t (*mount) (const char *drive); bool_t (*unmount) (const char *drive); bool_t (*sync) (GFILE *f); + #if GFILE_NEED_FILELISTS + gfileList * (*flopen) (const char *path, bool_t dirs); + const char *(*flread) (gfileList *pfl); + void (*flclose) (gfileList *pfl); + #endif } GFILEVMT; // The chain of FileSystems @@ -70,6 +80,9 @@ GFILE *gfileStdIn; GFILE *gfileStdOut; GFILE *gfileStdErr; +// Forward definition used by some special open calls +static GFILE *findemptyfile(const char *mode); + /** * The order of the file-systems below determines the order * that they are searched to find a file. @@ -119,6 +132,34 @@ GFILE *gfileStdErr; #endif /******************************************************** + * The virtual string file VMT + ********************************************************/ +#if GFILE_NEED_STRINGS + #include "src/gfile/inc_strings.c" +#endif + +/******************************************************** + * Printg Routines + ********************************************************/ +#if GFILE_NEED_PRINTG + #include "src/gfile/inc_printg.c" +#endif + +/******************************************************** + * Scang Routines + ********************************************************/ +#if GFILE_NEED_SCANG + #include "src/gfile/inc_scang.c" +#endif + +/******************************************************** + * Stdio Emulation Routines + ********************************************************/ +#if GFILE_NEED_STDIO + #include "src/gfile/inc_stdio.c" +#endif + +/******************************************************** * IO routines ********************************************************/ @@ -251,39 +292,48 @@ bool_t gfileRename(const char *oldname, const char *newname) { return FALSE; } -static uint16_t mode2flags(const char *mode) { - uint16_t flags; +static GFILE *findemptyfile(const char *mode) { + GFILE * f; - switch(mode[0]) { - case 'r': - flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; - while (*++mode) { - switch(mode[0]) { - case '+': flags |= GFILEFLG_WRITE; break; - case 'b': flags |= GFILEFLG_BINARY; break; - } - } - return flags; - case 'w': - flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; - while (*++mode) { - switch(mode[0]) { - case '+': flags |= GFILEFLG_READ; break; - case 'b': flags |= GFILEFLG_BINARY; break; - case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; - } - } - return flags; - case 'a': - flags = GFILEFLG_WRITE|GFILEFLG_APPEND; - while (*++mode) { + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Get the flags switch(mode[0]) { - case '+': flags |= GFILEFLG_READ; break; - case 'b': flags |= GFILEFLG_BINARY; break; - case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; + case 'r': + f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_WRITE; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + } + } + break; + case 'w': + f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + case 'a': + f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + default: + return 0; } + return f; } - return flags; } return 0; } @@ -307,112 +357,34 @@ static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) { } GFILE *gfileOpen(const char *fname, const char *mode) { - uint16_t flags; GFILE * f; const GFILEVMT *p; - // Get the requested mode - if (!(flags = mode2flags(mode))) + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) return 0; #if GFILE_ALLOW_DEVICESPECIFIC if (fname[0] && fname[1] == '|') { - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Try to open the file - f->flags = flags; - for(p = FsChain; p; p = p->next) { - if (p->prefix == fname[0]) - return testopen(p, f, fname+2) ? f : 0; - } - // File not found - break; - } + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return testopen(p, f, fname+2) ? f : 0; } - // No available slot + // File not found return 0; } #endif - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - - // Try to open the file - f->flags = flags; - for(p = FsChain; p; p = p->next) { - if (testopen(p, f, fname)) - return f; - } - // File not found - break; - } + for(p = FsChain; p; p = p->next) { + if (testopen(p, f, fname)) + return f; } - // No available slot + // File not found return 0; } -#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS - GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { - GFILE * f; - - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Get the flags - if (!(f->flags = mode2flags(mode))) - return 0; - - // If we want write but the fs doesn't allow it then return - if ((f->flags & GFILEFLG_WRITE) && !(FsCHIBIOSVMT.flags & GFSFLG_WRITEABLE)) - return 0; - - // File is open - fill in all the details - f->vmt = &FsCHIBIOSVMT; - f->obj = BaseFileStreamPtr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; - } - } - - // No available slot - return 0; - } -#endif - -#if GFILE_NEED_MEMFS - GFILE * gfileOpenMemory(void *memptr, const char *mode) { - GFILE * f; - - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Get the flags - if (!(f->flags = mode2flags(mode))) - return 0; - - // If we want write but the fs doesn't allow it then return - if ((f->flags & GFILEFLG_WRITE) && !(FsMemVMT.flags & GFSFLG_WRITEABLE)) - return 0; - - // File is open - fill in all the details - f->vmt = &FsMemVMT; - f->obj = memptr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; - } - } - - // No available slot - return 0; - } -#endif - void gfileClose(GFILE *f) { if (!f || !(f->flags & GFILEFLG_OPEN)) return; @@ -512,578 +484,34 @@ bool_t gfileSync(GFILE *f) { return f->vmt->sync(f); } -/******************************************************** - * String VMT routines - ********************************************************/ -#if GFILE_NEED_STRINGS && (GFILE_NEED_PRINTG || GFILE_NEED_SCANG) - #include <string.h> - - // Special String VMT - static int StringRead(GFILE *f, void *buf, int size) { - (void) size; - - // size must be 1 for a complete read - if (!((char *)f->obj)[f->pos]) - return 0; - ((char *)buf)[0] = ((char *)f->obj)[f->pos]; - return 1; - } - static int StringWrite(GFILE *f, const void *buf, int size) { - (void) size; - - // size must be 1 for a complete write - ((char *)f->obj)[f->pos] = ((char *)buf)[0]; - return 1; - } - static const GFILEVMT StringVMT = { - 0, // next - 0, // flags - '_', // prefix - 0, 0, 0, 0, - 0, 0, StringRead, StringWrite, - 0, 0, 0, - 0, 0 - }; -#endif - -/******************************************************** - * printg routines - ********************************************************/ -#if GFILE_NEED_PRINTG - #include <stdarg.h> - - #define MAX_FILLER 11 - #define FLOAT_PRECISION 100000 - - int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { - int res; - va_list ap; +#if GFILE_NEED_FILELISTS + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) { + const GFILEVMT *p; + gfileList * pfl; - va_start(ap, fmt); - res = vfnprintg(f, maxlen, fmt, ap); - va_end(ap); - return res; - } - - static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { - int i; - char * q; - - if (!divisor) divisor = num; - - q = p + MAX_FILLER; - do { - i = (int)(num % radix); - i += '0'; - if (i > '9') - i += 'A' - '0' - 10; - *--q = i; - num /= radix; - } while ((divisor /= radix) != 0); - - i = (int)(p + MAX_FILLER - q); - do { - *p++ = *q++; - } while (--i); - - return p; - } - - int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { - int ret; - char *p, *s, c, filler; - int i, precision, width; - bool_t is_long, left_align; - long l; - #if GFILE_ALLOW_FLOATS - float f; - char tmpbuf[2*MAX_FILLER + 1]; - #else - char tmpbuf[MAX_FILLER + 1]; - #endif - - ret = 0; - if (maxlen < 0) - return 0; - if (!maxlen) - maxlen = -1; - - while (*fmt) { - if (*fmt != '%') { - gfileWrite(f, fmt, 1); - ret++; if (!--maxlen) return ret; - fmt++; - continue; - } - fmt++; - - p = s = tmpbuf; - left_align = FALSE; - filler = ' '; - width = 0; - precision = 0; - - if (*fmt == '-') { - fmt++; - left_align = TRUE; - } - if (*fmt == '.') { - fmt++; - filler = '0'; - } - - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - width = width * 10 + c; - } else if (c == '*') - width = va_arg(arg, int); - else - break; - } - if (c == '.') { - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - precision = precision * 10 + c; - } else if (c == '*') - precision = va_arg(arg, int); - else - break; - } - } - /* Long modifier.*/ - if (c == 'l' || c == 'L') { - is_long = TRUE; - if (*fmt) - c = *fmt++; - } - else - is_long = (c >= 'A') && (c <= 'Z'); - - /* Command decoding.*/ - switch (c) { - case 0: - return ret; - case 'c': - filler = ' '; - *p++ = va_arg(arg, int); - break; - case 's': - filler = ' '; - if ((s = va_arg(arg, char *)) == 0) - s = "(null)"; - if (precision == 0) - precision = 32767; - for (p = s; *p && (--precision >= 0); p++); - break; - case 'D': - case 'd': - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - if (l < 0) { - *p++ = '-'; - l = -l; - } - p = ltoa_wd(p, l, 10, 0); - break; - #if GFILE_ALLOW_FLOATS - case 'f': - f = (float) va_arg(arg, double); - if (f < 0) { - *p++ = '-'; - f = -f; - } - l = f; - p = ltoa_wd(p, l, 10, 0); - *p++ = '.'; - l = (f - l) * FLOAT_PRECISION; - p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); - break; - #endif - case 'X': - case 'x': - c = 16; - goto unsigned_common; - case 'U': - case 'u': - c = 10; - goto unsigned_common; - case 'O': - case 'o': - c = 8; - unsigned_common: - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - p = ltoa_wd(p, l, c, 0); - break; - default: - *p++ = c; - break; - } - - i = (int)(p - s); - if ((width -= i) < 0) - width = 0; - if (left_align == FALSE) - width = -width; - if (width < 0) { - if (*s == '-' && filler == '0') { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - i--; - } - do { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - } while (++width != 0); - } - while (--i >= 0) { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - } - while (width) { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - width--; - } - } - return ret; - } - - #if GFILE_NEED_STRINGS - int snprintg(char *buf, int maxlen, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; - return 0; - } - maxlen += 1; - } - f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = buf; - va_start(ap, fmt); - res = vfnprintg(&f, maxlen-1, fmt, ap); - va_end(ap); - buf[res] = 0; - return res; - } - int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { - int res; - GFILE f; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; + // Find the correct VMT + for(p = FsChain; p; p = p->next) { + if (p->prefix == fs) { + if (!p->flopen) return 0; + pfl = p->flopen(path, dirs); + if (pfl) { + pfl->vmt = p; + pfl->dirs = dirs; } - maxlen += 1; + return pfl; } - f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = buf; - res = vfnprintg(&f, maxlen-1, fmt, arg); - buf[res] = 0; - return res; } - #endif -#endif - -/******************************************************** - * scang routines - ********************************************************/ -#if GFILE_NEED_SCANG - int fscang(GFILE *f, const char *fmt, ...) { - int res; - va_list ap; - - va_start(ap, fmt); - res = vfscang(f, fmt, ap); - va_end(ap); - return res; + return 0; } - int vfscang(GFILE *f, const char *fmt, va_list arg) { - int res, width, size, base; - unsigned long num; - char c; - bool_t assign, negate; - char *p; - - for(res = 0; *fmt; fmt++) { - switch(*fmt) { - case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': - break; - - case '%': - fmt++; - assign = TRUE; - negate = FALSE; - width = 0; - size = 1; - num = 0; - - if (*fmt == '*') { - fmt++; - assign = FALSE; - } - while(*fmt >= '0' && *fmt <= '9') - width = width * 10 + (*fmt++ - '0'); - if (*fmt == 'h') { - fmt++; - size = 0; - } else if (*fmt == 'l') { - fmt++; - size = 2; - } else if (*fmt == 'L') { - fmt++; - size = 3; - } - switch(*fmt) { - case 0: - return res; - case '%': - goto matchchar; - case 'c': - if (!width) { - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - width = 1; - } else { - if (!gfileRead(f, &c, 1)) return res; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - while(--width) { - if (!gfileRead(f, &c, 1)) return res; - if (assign) *p++ = c; - } - break; - case 's': - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - if (width) { - while(--width) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - if (assign) *p++ = c; - } - } else { - while(1) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': break; - default: - if (assign) *p++ = c; - continue; - } - break; - } - //ungetch(c); - } - if (assign) *p = 0; - break; - case 'd': base = 10; goto getnum; - case 'i': base = -1; goto getnum; - case 'o': base = 8; goto getnum; - case 'u': base = 10; goto getnum; - case 'x': base = 16; goto getnum; - case 'b': base = 2; - getnum: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c == '-' && *fmt != 'u') { - negate = TRUE; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - } - if (base == -1) { - if (c == '0') { - if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; - switch(c) { - case 'x': case 'X': - base = 16; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - case 'b': case 'B': - base = 2; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - default: - base = 8; - break; - } - } else - base = 10; - } - while(1) { - if (c >= '0' && c <= '9' && c - '0' < base) - num = num * base + (c - '0'); - else if (c >= 'A' && c <= 'F' && base == 16) - num = num * base + (c - ('A'-10)); - else if (c >= 'a' && c <= 'f' && base == 16) - num = num * base + (c - ('a'-10)); - else { - // ungetch(c) - break; - } - if ((width && !--width) || !gfileRead(f, &c, 1)) - break; - } - - assignnum: - if (negate) - num = -num; - - if (assign) { - switch(size) { - case 0: // short - p = (char *)va_arg(arg, short *); - res++; - *((short *)p) = (short)num; - case 1: // int - p = (char *)va_arg(arg, int *); - res++; - *((int *)p) = (int)num; - case 2: case 3: // long - p = (char *)va_arg(arg, long *); - res++; - *((long *)p) = (long)num; - } - } - break; - - #if GFILE_ALLOW_FLOATS - case 'e': case 'f': case 'g': - // TODO - #endif - default: - return res; - } - - break; - - default: - matchchar: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c != *fmt) return res; - break; - } - } - return res; + const char *gfileReadFileList(gfileList *pfl) { + return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0; } - #if GFILE_NEED_STRINGS - int sscang(const char *buf, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - f.flags = GFILEFLG_OPEN|GFILEFLG_READ; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = (void *)buf; - va_start(ap, fmt); - res = vfscang(&f, fmt, ap); - va_end(ap); - return res; - } - - int vsscang(const char *buf, const char *fmt, va_list arg) { - int res; - GFILE f; - - f.flags = GFILEFLG_OPEN|GFILEFLG_READ; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = (void *)buf; - res = vfscang(&f, fmt, arg); - return res; - } - #endif -#endif - -/******************************************************** - * stdio emulation routines - ********************************************************/ -#if GFILE_NEED_STDIO - size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { - return gfileRead(f, ptr, size*count)/size; - } - size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { - return gfileWrite(f, ptr, size*count)/size; - } - int gstdioSeek(FILE *f, size_t offset, int origin) { - switch(origin) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += f->pos; - break; - case SEEK_END: - offset += gfileGetSize(f); - break; - default: - return -1; - } - return gfileSetPos(f, offset) ? 0 : -1; - } - int gstdioGetpos(FILE *f, long int *pos) { - if (!(f->flags & GFILEFLG_OPEN)) - return -1; - *pos = f->pos; - return 0; + void gfileCloseFileList(gfileList *pfl) { + if (pfl->vmt->flclose) + pfl->vmt->flclose(pfl); } #endif diff --git a/src/gfile/inc_chibiosfs.c b/src/gfile/inc_chibiosfs.c index 8d321b33..13ae6cac 100644 --- a/src/gfile/inc_chibiosfs.c +++ b/src/gfile/inc_chibiosfs.c @@ -27,22 +27,39 @@ static const GFILEVMT FsCHIBIOSVMT = { 0, 0, 0, 0, 0, ChibiOSBFSClose, ChibiOSBFSRead, ChibiOSBFSWrite, ChibiOSBFSSetpos, ChibiOSBFSGetsize, ChibiOSBFSEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif }; static void ChibiOSBFSClose(GFILE *f) { - chFileStreamClose(((BaseFileStream *)f->fd)); + chFileStreamClose(((BaseFileStream *)f->obj)); } static int ChibiOSBFSRead(GFILE *f, void *buf, int size) { - return chSequentialStreamRead(((BaseFileStream *)f->fd), (uint8_t *)buf, size); + return chSequentialStreamRead(((BaseFileStream *)f->obj), (uint8_t *)buf, size); } static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size) { - return chSequentialStreamWrite(((BaseFileStream *)f->fd), (uint8_t *)buf, size); + return chSequentialStreamWrite(((BaseFileStream *)f->obj), (uint8_t *)buf, size); } static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos) { - chFileStreamSeek(((BaseFileStream *)f->fd), pos); + chFileStreamSeek(((BaseFileStream *)f->obj), pos); return TRUE; } -static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->fd)); } -static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->fd)); } +static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->obj)); } +static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->obj)); } + +GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { + GFILE * f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsCHIBIOSVMT; + f->obj = BaseFileStreamPtr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} diff --git a/src/gfile/inc_fatfs.c b/src/gfile/inc_fatfs.c index 8d7233e7..c8db0e64 100644 --- a/src/gfile/inc_fatfs.c +++ b/src/gfile/inc_fatfs.c @@ -30,6 +30,11 @@ static bool_t fatfsEOF(GFILE* f); static bool_t fatfsMount(const char* drive); static bool_t fatfsUnmount(const char* drive); static bool_t fatfsSync(GFILE* f); +#if _FS_MINIMIZE <= 1 && GFILE_NEED_FILELISTS + static gfileList *fatfsFlOpen(const char *path, bool_t dirs); + static const char *fatfsFlRead(gfileList *pfl); + static void fatfsFlClose(gfileList *pfl); +#endif static const GFILEVMT FsFatFSVMT = { GFILE_CHAINHEAD, @@ -46,14 +51,29 @@ static const GFILEVMT FsFatFSVMT = { fatfsSetPos, fatfsGetSize, fatfsEOF, - fatfsMount, - fatfsUnmount, - fatfsSync + fatfsMount, fatfsUnmount, fatfsSync, + #if GFILE_NEED_FILELISTS + #if _FS_MINIMIZE <= 1 + fatfsFlOpen, fatfsFlRead, fatfsFlClose + #else + 0, 0, 0 + #endif + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsFatFSVMT +// Our directory list structure +typedef struct fatfsList { + gfileList fl; // This must be the first element. + DIR dir; + FILINFO fno; + #if _USE_LFN + char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ + #endif +} fatfsList; + // optimize these later on. Use an array to have multiple FatFS static bool_t fatfs_mounted = FALSE; static FATFS fatfs_fs; @@ -245,3 +265,58 @@ static bool_t fatfsSync(GFILE *f) return TRUE; } +#if _FS_MINIMIZE <= 1 && GFILE_NEED_FILELISTS + static gfileList *fatfsFlOpen(const char *path, bool_t dirs) { + fatfsList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(fatfsList)))) + return 0; + + if (f_opendir(&p->dir, path) != FR_OK) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *fatfsFlRead(gfileList *pfl) { + #define ffl ((fatfsList *)pfl) + + while(1) { + #if _USE_LFN + ffl->fno.lfname = ffl->lfn; + ffl->fno.lfsize = sizeof(ffl->lfn); + #endif + + // Read the next entry + if (f_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) + return 0; + + /* Ignore dot entries */ + if (ffl->fno.fname[0] == '.') continue; + + /* Is it a directory */ + if (ffl->fl.dirs) { + if ((ffl->fno.fattrib & AM_DIR)) + break; + } else { + if (!(ffl->fno.fattrib & AM_DIR)) + break; + } + } + + #if _USE_LFN + return ffl->fno.lfname[0] ? ffl->fno.lfname : ffl->fno.fname; + #else + return ffl->fno.fname; + #endif + #undef ffl + } + + static void fatfsFlClose(gfileList *pfl) { + f_closedir(&((fatfsList *)pfl)->dir); + gfxFree(pfl); + } + +#endif diff --git a/src/gfile/inc_memfs.c b/src/gfile/inc_memfs.c index baeb0e97..6177b7d8 100644 --- a/src/gfile/inc_memfs.c +++ b/src/gfile/inc_memfs.c @@ -26,8 +26,10 @@ static const GFILEVMT FsMemVMT = { 0, 0, 0, 0, 0, 0, MEMRead, MEMWrite, MEMSetpos, 0, 0, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif }; static int MEMRead(GFILE *f, void *buf, int size) { @@ -43,3 +45,18 @@ static bool_t MEMSetpos(GFILE *f, long int pos) { (void) pos; return TRUE; } + +GFILE * gfileOpenMemory(void *memptr, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsMemVMT; + f->obj = memptr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} diff --git a/src/gfile/inc_nativefs.c b/src/gfile/inc_nativefs.c index 6845cb71..8c28480b 100644 --- a/src/gfile/inc_nativefs.c +++ b/src/gfile/inc_nativefs.c @@ -33,6 +33,11 @@ static int NativeWrite(GFILE *f, const void *buf, int size); static bool_t NativeSetpos(GFILE *f, long int pos); static long int NativeGetsize(GFILE *f); static bool_t NativeEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *NativeFlOpen(const char *path, bool_t dirs); + static const char *NativeFlRead(gfileList *pfl); + static void NativeFlClose(gfileList *pfl); +#endif static const GFILEVMT FsNativeVMT = { GFILE_CHAINHEAD, // next @@ -46,8 +51,10 @@ static const GFILEVMT FsNativeVMT = { NativeDel, NativeExists, NativeFilesize, NativeRen, NativeOpen, NativeClose, NativeRead, NativeWrite, NativeSetpos, NativeGetsize, NativeEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + NativeFlOpen, NativeFlRead, NativeFlClose + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsNativeVMT @@ -102,3 +109,108 @@ static long int NativeGetsize(GFILE *f) { if (fstat(fileno((FILE *)f->obj), &st)) return -1; return st.st_size; } + +#if GFILE_NEED_FILELISTS + #if defined(WIN32) || GFX_USE_OS_WIN32 + typedef struct NativeFileList { + gfileList fl; + HANDLE d; + WIN32_FIND_DATA f; + bool_t first; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if ((p->d = FindFirstFile(path, &p->f)) == INVALID_HANDLE_VALUE) { + gfxFree(p); + return 0; + } + p->first = TRUE; + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!nfl->first && !FindNextFile(nfl->d, &nfl->f)) + return 0; + nfl->first = FALSE; + if (nfl->f.cFileName[0] == '.') + continue; + if (nfl->fl.dirs) { + if ((nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } else { + if (!(nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } + } + return nfl->f.cFileName; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + CloseHandle(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + + #else + #include <dirent.h> + + typedef struct NativeFileList { + gfileList fl; + DIR * d; + struct dirent * f; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if (!(p->d = opendir(path))) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!(nfl->f = readdir(nfl->d))) + return 0; + if (nfl->f->d_name[0] == '.') + continue; + + #ifdef _DIRENT_HAVE_D_TYPE + if (nfl->fl.dirs) { + if (nfl->f->d_type == DT_DIR) + break; + } else { + if (nfl->f->d_type == DT_REG) + break; + } + #else + // Oops - no type field. We could use stat() here but that would mean + // concatting the supplied path to the found filename. + // That all just seems too hard. Instead we just don't + // distinguish between files and directories. + break; + #endif + } + return nfl->f->d_name; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + closedir(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + #endif +#endif diff --git a/src/gfile/inc_printg.c b/src/gfile/inc_printg.c new file mode 100644 index 00000000..8d24b347 --- /dev/null +++ b/src/gfile/inc_printg.c @@ -0,0 +1,261 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Printg Routines + ********************************************************/ + +#include <stdarg.h> + +#define MAX_FILLER 11 +#define FLOAT_PRECISION 100000 + +int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfnprintg(f, maxlen, fmt, ap); + va_end(ap); + return res; +} + +static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { + int i; + char * q; + + if (!divisor) divisor = num; + + q = p + MAX_FILLER; + do { + i = (int)(num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + num /= radix; + } while ((divisor /= radix) != 0); + + i = (int)(p + MAX_FILLER - q); + do { + *p++ = *q++; + } while (--i); + + return p; +} + +int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { + int ret; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if GFILE_ALLOW_FLOATS + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + ret = 0; + if (maxlen < 0) + return 0; + if (!maxlen) + maxlen = -1; + + while (*fmt) { + if (*fmt != '%') { + gfileWrite(f, fmt, 1); + ret++; if (!--maxlen) return ret; + fmt++; + continue; + } + fmt++; + + p = s = tmpbuf; + left_align = FALSE; + filler = ' '; + width = 0; + precision = 0; + + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + if (*fmt == '.') { + fmt++; + filler = '0'; + } + + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + width = width * 10 + c; + } else if (c == '*') + width = va_arg(arg, int); + else + break; + } + if (c == '.') { + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + precision = precision * 10 + c; + } else if (c == '*') + precision = va_arg(arg, int); + else + break; + } + } + /* Long modifier.*/ + if (c == 'l' || c == 'L') { + is_long = TRUE; + if (*fmt) + c = *fmt++; + } + else + is_long = (c >= 'A') && (c <= 'Z'); + + /* Command decoding.*/ + switch (c) { + case 0: + return ret; + case 'c': + filler = ' '; + *p++ = va_arg(arg, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(arg, char *)) == 0) + s = "(null)"; + if (precision == 0) + precision = 32767; + for (p = s; *p && (--precision >= 0); p++); + break; + case 'D': + case 'd': + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if GFILE_ALLOW_FLOATS + case 'f': + f = (float) va_arg(arg, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + l = f; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (f - l) * FLOAT_PRECISION; + p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); + break; + #endif + case 'X': + case 'x': + c = 16; + goto unsigned_common; + case 'U': + case 'u': + c = 10; + goto unsigned_common; + case 'O': + case 'o': + c = 8; + unsigned_common: + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + p = ltoa_wd(p, l, c, 0); + break; + default: + *p++ = c; + break; + } + + i = (int)(p - s); + if ((width -= i) < 0) + width = 0; + if (left_align == FALSE) + width = -width; + if (width < 0) { + if (*s == '-' && filler == '0') { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + i--; + } + do { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + } while (++width != 0); + } + while (--i >= 0) { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + } + while (width) { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + width--; + } + } + return ret; +} + +#if GFILE_NEED_STRINGS + int snprintg(char *buf, int maxlen, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + va_start(ap, fmt); + res = vfnprintg(&f, maxlen-1, fmt, ap); + va_end(ap); + return res; + } + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { + GFILE f; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + return vfnprintg(&f, maxlen-1, fmt, arg); + } +#endif diff --git a/src/gfile/inc_romfs.c b/src/gfile/inc_romfs.c index 167430ce..97d26239 100644 --- a/src/gfile/inc_romfs.c +++ b/src/gfile/inc_romfs.c @@ -31,11 +31,15 @@ typedef struct ROMFS_DIRENTRY { } ROMFS_DIRENTRY; #define ROMFS_DIRENTRY_HEAD 0 - #include "romfs_files.h" - static const ROMFS_DIRENTRY const *FsROMHead = ROMFS_DIRENTRY_HEAD; +typedef struct ROMFileList { + gfileList fl; + const ROMFS_DIRENTRY *pdir; +} ROMFileList; + + static bool_t ROMExists(const char *fname); static long int ROMFilesize(const char *fname); static bool_t ROMOpen(GFILE *f, const char *fname); @@ -44,6 +48,11 @@ static int ROMRead(GFILE *f, void *buf, int size); static bool_t ROMSetpos(GFILE *f, long int pos); static long int ROMGetsize(GFILE *f); static bool_t ROMEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs); + static const char *ROMFlRead(gfileList *pfl); + static void ROMFlClose(gfileList *pfl); +#endif static const GFILEVMT FsROMVMT = { GFILE_CHAINHEAD, // next @@ -52,8 +61,10 @@ static const GFILEVMT FsROMVMT = { 0, ROMExists, ROMFilesize, 0, ROMOpen, ROMClose, ROMRead, 0, ROMSetpos, ROMGetsize, ROMEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + ROMFlOpen, ROMFlRead, ROMFlClose + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsROMVMT @@ -122,3 +133,45 @@ static bool_t ROMEof(GFILE *f) { return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; } + +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs) { + ROMFileList * p; + (void) path; + + // We don't support directories or path searching + if (dirs) + return 0; + + // Allocate the list buffer + if (!(p = gfxAlloc(sizeof(ROMFileList)))) + return 0; + + // Initialize it and return it. + p->pdir = 0; + return &p->fl; + } + + static const char *ROMFlRead(gfileList *pfl) { + #define rfl ((ROMFileList *)pfl) + + // Is it the first entry + if (!rfl->pdir) { + rfl->pdir = FsROMHead; + return FsROMHead->name; + } + + // Is it not the last entry + if (rfl->pdir->next) { + rfl->pdir = rfl->pdir->next; + return rfl->pdir->name; + } + + return 0; + #undef rfl + } + + static void ROMFlClose(gfileList *pfl) { + gfxFree(pfl); + } +#endif diff --git a/src/gfile/inc_scang.c b/src/gfile/inc_scang.c new file mode 100644 index 00000000..8dcc8d0f --- /dev/null +++ b/src/gfile/inc_scang.c @@ -0,0 +1,257 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Scang Routines + ********************************************************/ + +int fscang(GFILE *f, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfscang(f, fmt, ap); + va_end(ap); + return res; +} + +int vfscang(GFILE *f, const char *fmt, va_list arg) { + int res, width, size, base; + unsigned long num; + char c; + bool_t assign, negate; + char *p; + + for(res = 0; *fmt; fmt++) { + switch(*fmt) { + case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': + break; + + case '%': + fmt++; + assign = TRUE; + negate = FALSE; + width = 0; + size = 1; + num = 0; + + if (*fmt == '*') { + fmt++; + assign = FALSE; + } + while(*fmt >= '0' && *fmt <= '9') + width = width * 10 + (*fmt++ - '0'); + if (*fmt == 'h') { + fmt++; + size = 0; + } else if (*fmt == 'l') { + fmt++; + size = 2; + } else if (*fmt == 'L') { + fmt++; + size = 3; + } + switch(*fmt) { + case 0: + return res; + case '%': + goto matchchar; + case 'c': + if (!width) { + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + width = 1; + } else { + if (!gfileRead(f, &c, 1)) return res; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + while(--width) { + if (!gfileRead(f, &c, 1)) return res; + if (assign) *p++ = c; + } + break; + case 's': + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + if (width) { + while(--width) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + if (assign) *p++ = c; + } + } else { + while(1) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': break; + default: + if (assign) *p++ = c; + continue; + } + break; + } + //ungetch(c); + } + if (assign) *p = 0; + break; + case 'd': base = 10; goto getnum; + case 'i': base = -1; goto getnum; + case 'o': base = 8; goto getnum; + case 'u': base = 10; goto getnum; + case 'x': base = 16; goto getnum; + case 'b': base = 2; + getnum: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c == '-' && *fmt != 'u') { + negate = TRUE; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + } + if (base == -1) { + if (c == '0') { + if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; + switch(c) { + case 'x': case 'X': + base = 16; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + case 'b': case 'B': + base = 2; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + default: + base = 8; + break; + } + } else + base = 10; + } + while(1) { + if (c >= '0' && c <= '9' && c - '0' < base) + num = num * base + (c - '0'); + else if (c >= 'A' && c <= 'F' && base == 16) + num = num * base + (c - ('A'-10)); + else if (c >= 'a' && c <= 'f' && base == 16) + num = num * base + (c - ('a'-10)); + else { + // ungetch(c) + break; + } + if ((width && !--width) || !gfileRead(f, &c, 1)) + break; + } + + assignnum: + if (negate) + num = -num; + + if (assign) { + switch(size) { + case 0: // short + p = (char *)va_arg(arg, short *); + res++; + *((short *)p) = (short)num; + case 1: // int + p = (char *)va_arg(arg, int *); + res++; + *((int *)p) = (int)num; + case 2: case 3: // long + p = (char *)va_arg(arg, long *); + res++; + *((long *)p) = (long)num; + } + } + break; + + #if GFILE_ALLOW_FLOATS + case 'e': case 'f': case 'g': + // TODO + #endif + default: + return res; + } + + break; + + default: + matchchar: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c != *fmt) return res; + break; + } + } + return res; +} + +#if GFILE_NEED_STRINGS + int sscang(const char *buf, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + va_start(ap, fmt); + res = vfscang(&f, fmt, ap); + va_end(ap); + return res; + } + + int vsscang(const char *buf, const char *fmt, va_list arg) { + GFILE f; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + return vfscang(&f, fmt, arg); + } +#endif diff --git a/src/gfile/inc_stdio.c b/src/gfile/inc_stdio.c new file mode 100644 index 00000000..8dc44dcb --- /dev/null +++ b/src/gfile/inc_stdio.c @@ -0,0 +1,45 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Stdio Emulation Routines + ********************************************************/ + +size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { + return gfileRead(f, ptr, size*count)/size; +} + +size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { + return gfileWrite(f, ptr, size*count)/size; +} + +int gstdioSeek(FILE *f, size_t offset, int origin) { + switch(origin) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += f->pos; + break; + case SEEK_END: + offset += gfileGetSize(f); + break; + default: + return -1; + } + return gfileSetPos(f, offset) ? 0 : -1; +} + +int gstdioGetpos(FILE *f, long int *pos) { + if (!(f->flags & GFILEFLG_OPEN)) + return -1; + *pos = f->pos; + return 0; +} diff --git a/src/gfile/inc_strings.c b/src/gfile/inc_strings.c new file mode 100644 index 00000000..692d2dd3 --- /dev/null +++ b/src/gfile/inc_strings.c @@ -0,0 +1,69 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The virtual string file VMT + ********************************************************/ + +#include <string.h> + +// Special String VMT +static int StringRead(GFILE *f, void *buf, int size) { + int res; + char *p; + + p = ((char *)f->obj) + f->pos; + for(res = 0; res < size && *p; res++, p++, buf = ((char *)buf)+1) + ((char *)buf)[0] = *p; + return res; +} +static int StringWrite(GFILE *f, const void *buf, int size) { + if ((f->flags & GFILEFLG_APPEND)) { + while(((char *)f->obj)[f->pos]) + f->pos++; + } + memcpy(((char *)f->obj)+f->pos, buf, size); + ((char *)f->obj)[f->pos+size] = 0; + return size; +} +static const GFILEVMT StringVMT = { + 0, // next + 0, // flags + '_', // prefix + 0, 0, 0, 0, + 0, 0, StringRead, StringWrite, + 0, 0, 0, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +static void gfileOpenStringFromStaticGFILE(GFILE *f, char *str) { + if ((f->flags & GFILEFLG_TRUNC)) + str[0] = 0; + f->vmt = &StringVMT; + f->obj = str; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; +} + +GFILE *gfileOpenString(char *str, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + gfileOpenStringFromStaticGFILE(f, str); + return f; +} diff --git a/src/gfile/sys_defs.h b/src/gfile/sys_defs.h index 0c5bac0c..81d72ac8 100644 --- a/src/gfile/sys_defs.h +++ b/src/gfile/sys_defs.h @@ -33,8 +33,10 @@ #ifndef GFILE_IMPLEMENTATION typedef void GFILE; + typedef void gfileList; #else typedef struct GFILE GFILE; + typedef struct gfileList gfileList; #endif extern GFILE *gfileStdIn; @@ -98,14 +100,33 @@ extern "C" { /** * @brief Open file * @details A file must be opened before it can be accessed - * @details ToDo (document possible modes) * @details The resulting GFILE will be used for all functions that access the file. * * @param[in] fname The file name - * @param[in] mode The mode + * @param[in] mode The mode. * * @return Valid GFILE on success, 0 otherwise * + * @note The modes follow the c library fopen() standard. + * The valid modes are:<br/> + * <ul><li>r - Open for read, the file must exist</li> + * <li>w - Open for write, the file is truncated if it exists</li> + * <li>wx - Open for write, the file must not exist</li> + * <li>a - Open for append, the file is truncated if it exists</li> + * <li>ax - Open for append, the file must not exists</li> + * </ul><br/> + * THe following flags can also be added to the above modes:<br/> + * <ul><li>+ - Open for both read and write</li> + * <li>b - Open as a binary file rather than a text file</li> + * <ul> + * @note Not all file-systems support all modes. For example, write + * is not available with the ROM file-system. Similarly few platforms + * distinguish between binary and text files. + * @note Even though binary vs text is relevant only for a small number of platforms + * the "b" flag should always be specified for binary files such as images. + * This ensures portability to other platforms. The extra flag will be ignored + * on platforms where it is not relevant. + * * @api */ GFILE * gfileOpen(const char *fname, const char *mode); @@ -239,15 +260,118 @@ extern "C" { */ bool_t gfileSync(GFILE *f); - #if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + #if GFILE_NEED_FILELISTS || defined(__DOXYGEN__) + /** + * @brief Open a file list + * + * @param[in] fs The file system (F for FatFS) + * @param[in] path Path information to pass to the file system + * @param[in] dirs Pass TRUE to get directories only, FALSE to get files only + * + * @return A pointer to a file list on success, NULL otherwise + * + * @note The path parameter is handled in a file-system specific way. It could be + * treated as a directory name, it may be treated as a file pattern, or it + * may be ignored. Passing NULL will always return the full list of files + * in at least the top level directory. + * @note For file systems that do not support directories, passing TRUE for dirs + * will return an error. + * @note You must call @p gfileCloseFileList() when you have finished with the + * file list in order to free resources. + * + * @api + */ + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs); + + /** + * @brief Get the next file in a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @return A pointer to a file (or directory) name. Returns NULL if there are no more. + * + * @note The file name may contain the full directory path or may not depending + * on how the file system treats directories. + * @note The returned buffer may be destroyed by the next call to any of + * @p gfileOpenFileList(), @p gfileReadFileList() or @p gfileCloseFileList(). + * Do not use this pointer after one of those calls. + * + * @api + */ + const char *gfileReadFileList(gfileList *pfl); + + /** + * @brief Close a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @api + */ + void gfileCloseFileList(gfileList *pfl); + #endif + + #if (GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS) || defined(__DOXYGEN__) + /** + * @brief Open file from a ChibiOS BaseFileStream + * + * @param[in] BaseFileStreamPtr The BaseFileStream to open as a GFILE + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). The + * open mode is NOT compared against the BaseFileStream capabilities. + * @note Supported operations are: read, write, getpos, setpos, eof and getsize + * + * @api + */ GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode); #endif - #if GFILE_NEED_MEMFS + #if GFILE_NEED_MEMFS || defined(__DOXYGEN__) + /** + * @brief Open file from a memory pointer + * + * @param[in] memptr The pointer to the memory + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Supported operations are: read, write, getpos, setpos + * + * @api + */ GFILE * gfileOpenMemory(void *memptr, const char *mode); #endif - #if GFILE_NEED_PRINTG + #if GFILE_NEED_STRINGS || defined(__DOXYGEN__) + /** + * @brief Open file from a null terminated C string + * + * @param[in] memptr The pointer to the string or string buffer + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Reading will return EOF when the NULL character is reached. + * @note Writing will always place a NULL in the next character effectively terminating the + * string at the character just written. + * @note Supported operations are: read, write, append, getpos, setpos + * @note Be careful with setpos and getpos. They do not check for the end of the string. + * @note Reading and Writing will read/write a maximum of one character at a time. + * + * @api + */ + GFILE * gfileOpenString(char *str, const char *mode); + #endif + + #if GFILE_NEED_PRINTG || defined(__DOXYGEN__) #include <stdarg.h> int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg); @@ -265,7 +389,7 @@ extern "C" { #endif #endif - #if GFILE_NEED_SCANG + #if GFILE_NEED_SCANG || defined(__DOXYGEN__) #include <stdarg.h> int vfscang(GFILE *f, const char *fmt, va_list arg); diff --git a/src/gfile/sys_options.h b/src/gfile/sys_options.h index ee52298c..ff1e5d4c 100644 --- a/src/gfile/sys_options.h +++ b/src/gfile/sys_options.h @@ -46,6 +46,7 @@ /** * @brief Include printg, fprintg etc functions * @details Defaults to FALSE + * @pre To get the string sprintg functions you also need to define @p GFILE_NEED_STRINGS */ #ifndef GFILE_NEED_PRINTG #define GFILE_NEED_PRINTG FALSE @@ -53,15 +54,14 @@ /** * @brief Include scang, fscang etc functions * @details Defaults to FALSE + * @pre To get the string sscang functions you also need to define @p GFILE_NEED_STRINGS */ #ifndef GFILE_NEED_SCANG #define GFILE_NEED_SCANG FALSE #endif /** - * @brief Include the string sprintg/sscang functions + * @brief Include the string based file functions * @details Defaults to FALSE - * @pre To get sprintg functions you also need to define @p GFILE_NEED_PRINTG - * @pre To get sscang functions you also need to define @p GFILE_NEED_SCANG */ #ifndef GFILE_NEED_STRINGS #define GFILE_NEED_STRINGS FALSE @@ -146,6 +146,14 @@ #ifndef GFILE_NEED_MEMFS #define GFILE_NEED_MEMFS FALSE #endif + /** + * @brief Include support for file list functions + * @details Defaults to FALSE + * @note Adds support for @p gfileOpenFileList(), @p gfileReadFileList() and @p gfileCloseFileList(). + */ + #ifndef GFILE_NEED_FILELISTS + #define GFILE_NEED_FILELISTS FALSE + #endif /** * @} * diff --git a/src/gos/chibios.c b/src/gos/gfx_chibios.c index 468c012c..9d1a86da 100644 --- a/src/gos/chibios.c +++ b/src/gos/gfx_chibios.c @@ -33,18 +33,21 @@ void _gosInit(void) { - /* Don't initialise if the user already has */ - - #if CH_KERNEL_MAJOR == 2 - if (!chThdSelf()) { - halInit(); - chSysInit(); - } - #elif CH_KERNEL_MAJOR == 3 - if (!chThdGetSelfX()) { - halInit(); - chSysInit(); - } + #if !GFX_NO_OS_INIT + /* Don't Initialize if the user already has */ + #if CH_KERNEL_MAJOR == 2 + if (!chThdSelf()) { + halInit(); + chSysInit(); + } + #elif CH_KERNEL_MAJOR == 3 + if (!chThdGetSelfX()) { + halInit(); + chSysInit(); + } + #endif + #else + #warning "GOS: Operating System initialization has been turned off. Make sure you call halInit() and chSysInit() before gfxInit() in your application!" #endif } diff --git a/src/gos/chibios.h b/src/gos/gfx_chibios.h index a07c72ab..a07c72ab 100644 --- a/src/gos/chibios.h +++ b/src/gos/gfx_chibios.h diff --git a/src/gos/ecos.c b/src/gos/gfx_ecos.c index 5b94497a..16ce821b 100644 --- a/src/gos/ecos.c +++ b/src/gos/gfx_ecos.c @@ -11,8 +11,11 @@ void _gosInit(void) { - /* Don't initialise if the user already has */ - //cyg_scheduler_start(); + #if !GFX_NO_OS_INIT + #error "GOS: Operating System initialization for eCos is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" + #else + #warning "GOS: Operating System initialization has been turned off. Make sure you call cyg_scheduler_start() before gfxInit() in your application!" + #endif } void _gosDeinit(void) diff --git a/src/gos/ecos.h b/src/gos/gfx_ecos.h index be9037b6..be9037b6 100644 --- a/src/gos/ecos.h +++ b/src/gos/gfx_ecos.h diff --git a/src/gos/freertos.c b/src/gos/gfx_freertos.c index f2c03eec..dbdfd22e 100644 --- a/src/gos/freertos.c +++ b/src/gos/gfx_freertos.c @@ -18,13 +18,17 @@ #error "GOS: configUSE_MUTEXES must be defined in FreeRTOSConfig.h" #endif -#if configUSE_COUNTING_SEMAPHORES != 1 +#if configUSE_COUNTING_SEMAPHORES != 1 #error "GOS: configUSE_COUNTING_SEMAPHORES must be defined in FreeRTOSConfig.h" #endif void _gosInit(void) { - // The user must call vTaskStartScheduler() himself before he calls gfxInit(). + #if !GFX_NO_OS_INIT + #error "GOS: Operating System initialization for FreeRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" + #else + #warning "GOS: Operating System initialization has been turned off. Make sure you call vTaskStartScheduler() before gfxInit() in your application!" + #endif } void _gosDeinit(void) diff --git a/src/gos/freertos.h b/src/gos/gfx_freertos.h index ccda4cbd..34ef548e 100644 --- a/src/gos/freertos.h +++ b/src/gos/gfx_freertos.h @@ -24,6 +24,18 @@ /* Type definitions */ /*===========================================================================*/ +/* Additional types are required when FreeRTOS 7.x is used */ +#if !defined(tskKERNEL_VERSION_MAJOR) && !tskKERNEL_VERSION_MAJOR == 8 + typedef signed char int8_t + typedef unsigned char uint8_t + typedef signed int int16_t + typedef unsigned int uint16_t + typedef signed long int int32_t + typedef unsigned long int uint32_t + typedef signed long long int int64_t + typedef unsigned long long int uint64_t +#endif + /** * bool_t, * int8_t, uint8_t, diff --git a/src/gos/linux.c b/src/gos/gfx_linux.c index d127fbe1..59b7f9c8 100644 --- a/src/gos/linux.c +++ b/src/gos/gfx_linux.c @@ -18,6 +18,7 @@ static gfxMutex SystemMutex; void _gosInit(void) { + /* No initialization of the operating system itself is needed */ gfxMutexInit(&SystemMutex); } diff --git a/src/gos/linux.h b/src/gos/gfx_linux.h index 9ead9c0e..9ead9c0e 100644 --- a/src/gos/linux.h +++ b/src/gos/gfx_linux.h diff --git a/src/gos/osx.c b/src/gos/gfx_osx.c index dccd49c9..50b06530 100644 --- a/src/gos/osx.c +++ b/src/gos/gfx_osx.c @@ -35,6 +35,7 @@ void get_ticks(mach_timespec_t *mts){ void _gosInit(void) { + /* No initialization of the operating system itself is needed */ gfxMutexInit(&SystemMutex); } diff --git a/src/gos/osx.h b/src/gos/gfx_osx.h index 635a8934..635a8934 100644 --- a/src/gos/osx.h +++ b/src/gos/gfx_osx.h diff --git a/src/gos/raw32.c b/src/gos/gfx_raw32.c index c75342d4..22c753aa 100644 --- a/src/gos/raw32.c +++ b/src/gos/gfx_raw32.c @@ -24,6 +24,12 @@ static void _gosThreadsInit(void); void _gosInit(void) { + /* No initialization of the operating system itself is needed as there isn't one. + * On the other hand the C runtime should still already be initialized before + * getting here! + */ + #warning "GOS: Raw32 - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + // Set up the heap allocator _gosHeapInit(); diff --git a/src/gos/raw32.h b/src/gos/gfx_raw32.h index 6eb5f26e..5a6a2aa7 100644 --- a/src/gos/raw32.h +++ b/src/gos/gfx_raw32.h @@ -42,14 +42,20 @@ /*===========================================================================*/ typedef unsigned char bool_t; -typedef char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef int int32_t; -typedef unsigned int uint32_t; - -typedef uint32_t size_t; + +#ifndef _STDINT_H + typedef char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; +#endif + +#if !defined (__need_size_t) && !defined (_STDDEF_H_) + typedef uint32_t size_t; +#endif + typedef uint32_t delaytime_t; typedef uint32_t systemticks_t; typedef short semcount_t; diff --git a/src/gos/gfx_rawrtos.c b/src/gos/gfx_rawrtos.c new file mode 100644 index 00000000..cd684208 --- /dev/null +++ b/src/gos/gfx_rawrtos.c @@ -0,0 +1,83 @@ +#include "gfx.h" + +#if GFX_USE_OS_RAWRTOS + +#include <string.h> +#include "raw_api.h" +#include "raw_config.h" + +#if CONFIG_RAW_MUTEX != 1 + #error "GOS: CONFIG_RAW_MUTEX must be defined in raw_config.h" +#endif + +#if CONFIG_RAW_SEMAPHORE != 1 + #error "GOS: CONFIG_RAW_SEMAPHORE must be defined in raw_config.h" +#endif + + +void _gosInit(void) +{ + #if !GFX_NO_OS_INIT + #error "GOS: Operating System initialization for RawRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" + #else + #warning "GOS: Operating System initialization has been turned off. Make sure you call raw_os_start() before gfxInit() in your application!" + #endif +} + +void _gosDeinit(void) +{ +} + + +void gfxSleepMilliseconds(delaytime_t ms) +{ + systemticks_t ticks = ms*RAW_TICKS_PER_SECOND/1000; + if(!ticks)ticks = 1; + raw_sleep(ticks); +} + +void gfxSleepMicroseconds(delaytime_t us) +{ + systemticks_t ticks = (us/1000)*RAW_TICKS_PER_SECOND/1000; + if(!ticks)ticks = 1; + raw_sleep(ticks); +} + +bool_t gfxSemWait(gfxSem* psem, delaytime_t ms) +{ + systemticks_t ticks = ms*RAW_TICKS_PER_SECOND/1000; + if(!ticks)ticks=1; + if(raw_semaphore_get((psem), ticks)==RAW_SUCCESS) + return TRUE; + return FALSE; +} + +bool_t gfxSemWaitI(gfxSem* psem) +{ + if(raw_semaphore_get((psem), TIME_IMMEDIATE)==RAW_SUCCESS) + return TRUE; + return FALSE; +} + +gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) +{ + RAW_U16 ret; + gfxThreadHandle taskobj; + + taskobj = gfxAlloc(sizeof(RAW_TASK_OBJ)); + ret = raw_task_create(taskobj, (RAW_U8 *)"uGFX_TASK", param, + prio, 0, stackarea, + stacksz/sizeof(PORT_STACK) , fn, 1); + + if (ret != RAW_SUCCESS) { + for (;;); + } + + return (taskobj); +} + + +#endif + + + diff --git a/src/gos/gfx_rawrtos.h b/src/gos/gfx_rawrtos.h new file mode 100644 index 00000000..eeb5119d --- /dev/null +++ b/src/gos/gfx_rawrtos.h @@ -0,0 +1,77 @@ +#ifndef _GOS_RAWRTOS_H +#define _GOS_RAWRTOS_H + +#if GFX_USE_OS_RAWRTOS + +#include "raw_api.h" +#include <stdint.h> + +#define TIME_IMMEDIATE (RAW_NO_WAIT) +#define TIME_INFINITE (RAW_WAIT_FOREVER) +typedef int8_t bool_t; +typedef uint32_t delaytime_t; +typedef RAW_TICK_TYPE systemticks_t; +typedef int32_t semcount_t; +typedef uint32_t threadreturn_t; +typedef RAW_U8 threadpriority_t; +typedef uint32_t size_t; + +#define MAX_SEMAPHORE_COUNT RAW_SEMAPHORE_COUNT +#define LOW_PRIORITY (CONFIG_RAW_PRIO_MAX-2) +#define NORMAL_PRIORITY (CONFIG_RAW_PRIO_MAX/2) +#define HIGH_PRIORITY 1 + +typedef RAW_SEMAPHORE gfxSem; +typedef RAW_MUTEX gfxMutex; +typedef RAW_TASK_OBJ* gfxThreadHandle; + +#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) +#define DECLARE_THREAD_STACK(name, sz) PORT_STACK name[sz]; + +#define gfxHalt(msg) for(;;) +#define gfxExit() for(;;) +#define gfxAlloc(sz) raw_malloc(sz) +#define gfxRealloc(p,osz,nsz) raw_calloc(p, nsz) +#define gfxFree(ptr) raw_free(ptr) +#define gfxYield() raw_sleep(0) +#define gfxSystemTicks() raw_system_time_get() +#define gfxMillisecondsToTicks(ms) (ms*RAW_TICKS_PER_SECOND/1000) +#define gfxSystemLock() {} +#define gfxSystemUnlock() {} +#define gfxMutexInit(pmutex) raw_mutex_create(pmutex, (RAW_U8 *)"", RAW_MUTEX_INHERIT_POLICY, 3) +#define gfxMutexDestroy(pmutex) raw_mutex_delete(pmutex) +#define gfxMutexEnter(pmutex) raw_mutex_get(pmutex, TIME_INFINITE) +#define gfxMutexExit(pmutex) raw_mutex_put(pmutex) +#define gfxSemInit(psem, val, limit) raw_semaphore_create(psem, "", val) +#define gfxSemDestroy(psem) raw_semaphore_delete(psem) +#define gfxSemSignal(psem) raw_semaphore_put((psem)) +#define gfxSemSignalI(psem) raw_semaphore_put_all((psem)) +#define gfxSemCounterI(psem) ((psem)->count) +#define gfxThreadMe() {(unsigned int)raw_task_identify()} +#define gfxThreadClose(thread) {} + +extern RAW_VOID *raw_malloc(RAW_U32 size); +extern RAW_VOID raw_free(void *ptr); +extern RAW_VOID *raw_calloc(RAW_U32 nmemb, RAW_U32 size); + +extern RAW_U16 raw_sleep(RAW_TICK_TYPE dly); +extern RAW_TICK_TYPE raw_system_time_get(void); + +extern RAW_U16 raw_mutex_create(RAW_MUTEX *mutex_ptr, RAW_U8 *name_ptr, RAW_U8 policy, RAW_U8 ceiling_prio); +extern RAW_U16 raw_mutex_delete(RAW_MUTEX *mutex_ptr); +extern RAW_U16 raw_mutex_get(RAW_MUTEX *mutex_ptr, RAW_TICK_TYPE wait_option); +extern RAW_U16 raw_mutex_put(RAW_MUTEX *mutex_ptr); +extern RAW_U16 raw_semaphore_create(RAW_SEMAPHORE *semaphore_ptr, RAW_U8 *name_ptr, RAW_U32 initial_count); +extern RAW_U16 raw_semaphore_delete(RAW_SEMAPHORE *semaphore_ptr); +extern RAW_U16 raw_semaphore_get(RAW_SEMAPHORE *semaphore_ptr, RAW_TICK_TYPE wait_option); +extern RAW_U16 raw_semaphore_put(RAW_SEMAPHORE *semaphore_ptr); + +void gfxSleepMilliseconds(delaytime_t ms); +void gfxSleepMicroseconds(delaytime_t us); +bool_t gfxSemWait(gfxSem* psem, delaytime_t ms); +bool_t gfxSemWaitI(gfxSem* psem); +gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); + +#endif + +#endif diff --git a/src/gos/win32.c b/src/gos/gfx_win32.c index 3a3f2517..ffa7fac5 100644 --- a/src/gos/win32.c +++ b/src/gos/gfx_win32.c @@ -19,7 +19,7 @@ static HANDLE SystemMutex; void _gosInit(void) { - + /* No initialization of the operating system itself is needed */ } void _gosDeinit(void) diff --git a/src/gos/win32.h b/src/gos/gfx_win32.h index 4a198200..4a198200 100644 --- a/src/gos/win32.h +++ b/src/gos/gfx_win32.h diff --git a/src/gos/sys_defs.h b/src/gos/sys_defs.h index 9da9dff0..d116826f 100644 --- a/src/gos/sys_defs.h +++ b/src/gos/sys_defs.h @@ -439,20 +439,22 @@ * All the above was just for the doxygen documentation. All the implementation of the above * (without any of the documentation overheads) is in the files below. */ +#elif GFX_USE_OS_RAWRTOS + #include "src/gos/gfx_rawrtos.h" #elif GFX_USE_OS_CHIBIOS - #include "src/gos/chibios.h" + #include "src/gos/gfx_chibios.h" #elif GFX_USE_OS_FREERTOS - #include "src/gos/freertos.h" + #include "src/gos/gfx_freertos.h" #elif GFX_USE_OS_WIN32 - #include "src/gos/win32.h" + #include "src/gos/gfx_win32.h" #elif GFX_USE_OS_LINUX - #include "src/gos/linux.h" + #include "src/gos/gfx_linux.h" #elif GFX_USE_OS_OSX - #include "src/gos/osx.h" + #include "src/gos/gfx_osx.h" #elif GFX_USE_OS_RAW32 - #include "src/gos/raw32.h" + #include "src/gos/gfx_raw32.h" #elif GFX_USE_OS_ECOS - #include "src/gos/ecos.h" + #include "src/gos/gfx_ecos.h" #else #error "Your operating system is not supported yet" #endif diff --git a/src/gos/sys_make.mk b/src/gos/sys_make.mk index 9e24f875..5efa7f80 100644 --- a/src/gos/sys_make.mk +++ b/src/gos/sys_make.mk @@ -1,8 +1,9 @@ -GFXSRC += $(GFXLIB)/src/gos/chibios.c \ - $(GFXLIB)/src/gos/freertos.c \ - $(GFXLIB)/src/gos/win32.c \ - $(GFXLIB)/src/gos/linux.c \ - $(GFXLIB)/src/gos/osx.c \ - $(GFXLIB)/src/gos/raw32.c \ - $(GFXLIB)/src/gos/ecos.c +GFXSRC += $(GFXLIB)/src/gos/gfx_chibios.c \ + $(GFXLIB)/src/gos/gfx_freertos.c \ + $(GFXLIB)/src/gos/gfx_win32.c \ + $(GFXLIB)/src/gos/gfx_linux.c \ + $(GFXLIB)/src/gos/gfx_osx.c \ + $(GFXLIB)/src/gos/gfx_raw32.c \ + $(GFXLIB)/src/gos/gfx_ecos.c \ + $(GFXLIB)/src/gos/gfx_rawrtos.c diff --git a/src/gos/sys_options.h b/src/gos/sys_options.h index 7937e082..ead1f3f7 100644 --- a/src/gos/sys_options.h +++ b/src/gos/sys_options.h @@ -76,6 +76,20 @@ * @{ */ /** + * @brief Should uGFX avoid initializing the operating system + * @details Defaults to FALSE + * @note This is not relevant to all operating systems eg Win32 never initializes the + * operating system as uGFX runs as an application outside the boot process. + * @note Operating system initialization is not necessarily implemented for all + * operating systems yet even when it is relevant. These operating systems + * will display a compile warning reminding you to initialize the operating + * system in your application code. Note that on these operating systems the + * demo applications will not work without modification. + */ + #ifndef GFX_NO_OS_INIT + #define GFX_NO_OS_INIT FALSE + #endif + /** * @brief Should uGFX stuff be added to the FreeRTOS+Tracer * @details Defaults to FALSE */ diff --git a/src/gos/sys_rules.h b/src/gos/sys_rules.h index 0da01ff2..6d6c7845 100644 --- a/src/gos/sys_rules.h +++ b/src/gos/sys_rules.h @@ -16,7 +16,7 @@ #ifndef _GOS_RULES_H #define _GOS_RULES_H -#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX && !GFX_USE_OS_RAW32 && !GFX_USE_OS_FREERTOS && !GFX_USE_OS_ECOS +#if !GFX_USE_OS_CHIBIOS && !GFX_USE_OS_WIN32 && !GFX_USE_OS_LINUX && !GFX_USE_OS_OSX && !GFX_USE_OS_RAW32 && !GFX_USE_OS_FREERTOS && !GFX_USE_OS_ECOS && !GFX_USE_OS_RAWRTOS #if GFX_DISPLAY_RULE_WARNINGS #warning "GOS: No Operating System has been defined. ChibiOS (GFX_USE_OS_CHIBIOS) has been turned on for you." #endif @@ -24,7 +24,7 @@ #define GFX_USE_OS_CHIBIOS TRUE #endif -#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX + GFX_USE_OS_RAW32 + GFX_USE_OS_FREERTOS + GFX_USE_OS_ECOS != 1 * TRUE +#if GFX_USE_OS_CHIBIOS + GFX_USE_OS_WIN32 + GFX_USE_OS_LINUX + GFX_USE_OS_OSX + GFX_USE_OS_RAW32 + GFX_USE_OS_FREERTOS + GFX_USE_OS_ECOS + GFX_USE_OS_RAWRTOS != 1 * TRUE #error "GOS: More than one operation system has been defined as TRUE." #endif diff --git a/src/gwin/button.c b/src/gwin/button.c index d489ecb0..fc1cb976 100644 --- a/src/gwin/button.c +++ b/src/gwin/button.c @@ -39,6 +39,9 @@ static void SendButtonEvent(GWidgetObject *gw) { continue; pbe->type = GEVENT_GWIN_BUTTON; pbe->button = (GHandle)gw; + #if GWIN_WIDGET_TAGS + pbe->tag = gw->tag; + #endif geventSendEvent(psl); } diff --git a/src/gwin/button.h b/src/gwin/button.h index 73d5f9f1..077b50f4 100644 --- a/src/gwin/button.h +++ b/src/gwin/button.h @@ -38,6 +38,9 @@ typedef struct GEventGWinButton { GEventType type; // The type of this event (GEVENT_GWIN_BUTTON) GHandle button; // The button that has been depressed (actually triggered on release) + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The button tag + #endif } GEventGWinButton; /** diff --git a/src/gwin/checkbox.c b/src/gwin/checkbox.c index f162d8fc..7914ee82 100644 --- a/src/gwin/checkbox.c +++ b/src/gwin/checkbox.c @@ -33,6 +33,9 @@ static void SendCheckboxEvent(GWidgetObject *gw) { pce->type = GEVENT_GWIN_CHECKBOX; pce->checkbox = &gw->g; pce->isChecked = (gw->g.flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; + #if GWIN_WIDGET_TAGS + pce->tag = gw->tag; + #endif geventSendEvent(psl); } diff --git a/src/gwin/checkbox.h b/src/gwin/checkbox.h index 2b1fb801..ebd35a0b 100644 --- a/src/gwin/checkbox.h +++ b/src/gwin/checkbox.h @@ -38,6 +38,9 @@ typedef struct GEventGWinCheckbox { GEventType type; // The type of this event (GEVENT_GWIN_CHECKBOX) GHandle checkbox; // The checkbox that has been depressed (actually triggered on release) bool_t isChecked; // Is the checkbox currently checked or unchecked? + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The checkbox tag + #endif } GEventGWinCheckbox; /* A Checkbox window */ diff --git a/src/gwin/class_gwin.h b/src/gwin/class_gwin.h index 995121b7..b32e4da2 100644 --- a/src/gwin/class_gwin.h +++ b/src/gwin/class_gwin.h @@ -23,6 +23,10 @@ #if GFX_USE_GWIN || defined(__DOXYGEN__) +#if defined(__KEIL__) || defined(__C51__) + #pragma anon_unions +#endif + /** * @brief The predefined flags for a Window * @{ diff --git a/src/gwin/frame.c b/src/gwin/frame.c index c10aaea3..3f41c69f 100644 --- a/src/gwin/frame.c +++ b/src/gwin/frame.c @@ -38,8 +38,10 @@ static coord_t BorderSizeT(GHandle gh) { return (gh->flags & GWIN_FRAME_BORDER) static void _frameDestroy(GHandle gh) { /* Deregister the button callback */ - geventRegisterCallback(&gh2obj->gl, NULL, NULL); - geventDetachSource(&gh2obj->gl, NULL); + if ((gh->flags & (GWIN_FRAME_CLOSE_BTN|GWIN_FRAME_MINMAX_BTN))) { + geventRegisterCallback(&gh2obj->gl, NULL, NULL); + geventDetachSource(&gh2obj->gl, NULL); + } /* call the gcontainer standard destroy routine */ _gcontainerDestroy(gh); diff --git a/src/gwin/gcontainer.c b/src/gwin/gcontainer.c index 46e89032..2d711ffd 100644 --- a/src/gwin/gcontainer.c +++ b/src/gwin/gcontainer.c @@ -93,11 +93,14 @@ coord_t gwinGetInnerHeight(GHandle gh) { static coord_t BorderSize(GHandle gh) { return (gh->flags & GWIN_CONTAINER_BORDER) ? 2 : 0; } static void DrawSimpleContainer(GWidgetObject *gw, void *param) { - (void) param; - gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); - if ((gw->g.flags & GWIN_CONTAINER_BORDER)) - gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); -} + (void)param; + + if (!(gw->g.flags & GWIN_CONTAINER_TRANSPARENT)) + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->background); + + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) + gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, (gw->g.flags & GWIN_FLG_SYSENABLED) ? gw->pstyle->enabled.edge : gw->pstyle->disabled.edge); +} // The container VMT table static const gcontainerVMT containerVMT = { diff --git a/src/gwin/gcontainer.h b/src/gwin/gcontainer.h index efba83f9..942cf8c0 100644 --- a/src/gwin/gcontainer.h +++ b/src/gwin/gcontainer.h @@ -105,6 +105,7 @@ extern "C" { * @{ */ #define GWIN_CONTAINER_BORDER 0x00000001 + #define GWIN_CONTAINER_TRANSPARENT 0x00000002 /** @} */ /** diff --git a/src/gwin/gwidget.c b/src/gwin/gwidget.c index 8ccb47fc..c9fff50e 100644 --- a/src/gwin/gwidget.c +++ b/src/gwin/gwidget.c @@ -86,6 +86,7 @@ static void gwidgetEvent(void *param, GEvent *pe) { #define pte ((GEventToggle *)pe) #define pde ((GEventDial *)pe) + GHandle h; GHandle gh; #if GFX_USE_GINPUT && (GINPUT_NEED_TOGGLE || GINPUT_NEED_DIAL) uint16_t role; @@ -99,32 +100,38 @@ static void gwidgetEvent(void *param, GEvent *pe) { case GEVENT_MOUSE: case GEVENT_TOUCH: // Cycle through all windows - for(gh = gwinGetNextWindow(0); gh; gh = gwinGetNextWindow(gh)) { - - // check if the widget matches this display - if (gh->display != pme->display) - continue; + for(gh = 0, h = gwinGetNextWindow(0); h; h = gwinGetNextWindow(h)) { - // check if it is a widget that is enabled and visible - if ((gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) != (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED|GWIN_FLG_SYSVISIBLE)) + // The window must be on this display and visible to be relevant + if (h->display != pme->display || !(h->flags & GWIN_FLG_SYSVISIBLE)) continue; - // Are we captured? - if ((gw->g.flags & GWIN_FLG_MOUSECAPTURE)) { + // Is the mouse currently captured by this widget? + if ((h->flags & (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) == (GWIN_FLG_WIDGET|GWIN_FLG_MOUSECAPTURE)) { + gh = h; if ((pme->last_buttons & ~pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { - gw->g.flags &= ~GWIN_FLG_MOUSECAPTURE; + gh->flags &= ~GWIN_FLG_MOUSECAPTURE; if (wvmt->MouseUp) - wvmt->MouseUp(gw, pme->x - gw->g.x, pme->y - gw->g.y); + wvmt->MouseUp(gw, pme->x - gh->x, pme->y - gh->y); } else if (wvmt->MouseMove) - wvmt->MouseMove(gw, pme->x - gw->g.x, pme->y - gw->g.y); + wvmt->MouseMove(gw, pme->x - gh->x, pme->y - gh->y); + + // There is only ever one captured mouse. Prevent normal mouse processing if there is a captured mouse + gh = 0; + break; + } + + // Save the highest z-order window that the mouse is over + if (pme->x >= h->x && pme->x < h->x + h->width && pme->y >= h->y && pme->y < h->y + h->height) + gh = h; + } - // We are not captured - look for mouse downs over the widget - } else if ((~pme->last_buttons & pme->current_buttons & GINPUT_MOUSE_BTN_LEFT) - && pme->x >= gw->g.x && pme->x < gw->g.x + gw->g.width - && pme->y >= gw->g.y && pme->y < gw->g.y + gw->g.height) { - gw->g.flags |= GWIN_FLG_MOUSECAPTURE; + // Process any mouse down over the highest order window if it is an enabled widget + if (gh && (gh->flags & (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) == (GWIN_FLG_WIDGET|GWIN_FLG_SYSENABLED)) { + if ((~pme->last_buttons & pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)) { + gh->flags |= GWIN_FLG_MOUSECAPTURE; if (wvmt->MouseDown) - wvmt->MouseDown(gw, pme->x - gw->g.x, pme->y - gw->g.y); + wvmt->MouseDown(gw, pme->x - gh->x, pme->y - gh->y); } } break; @@ -242,6 +249,9 @@ GHandle _gwidgetCreate(GDisplay *g, GWidgetObject *pgw, const GWidgetInit *pInit pgw->fnDraw = pInit->customDraw ? pInit->customDraw : vmt->DefaultDraw; pgw->fnParam = pInit->customParam; pgw->pstyle = pInit->customStyle ? pInit->customStyle : defaultStyle; + #if GWIN_WIDGET_TAGS + pgw->tag = pInit->tag; + #endif return &pgw->g; } @@ -473,5 +483,16 @@ bool_t gwinAttachListener(GListener *pl) { } #endif +#if GWIN_WIDGET_TAGS + void gwinSetTag(GHandle gh, WidgetTag tag) { + if ((gh->flags & GWIN_FLG_WIDGET)) + gw->tag = tag; + } + + WidgetTag gwinGetTag(GHandle gh) { + return ((gh->flags & GWIN_FLG_WIDGET)) ? gw->tag : 0; + } +#endif + #endif /* GFX_USE_GWIN && GWIN_NEED_WIDGET */ /** @} */ diff --git a/src/gwin/gwidget.h b/src/gwin/gwidget.h index 0a7bc72f..bd1ea4c8 100644 --- a/src/gwin/gwidget.h +++ b/src/gwin/gwidget.h @@ -73,6 +73,11 @@ extern const GWidgetStyle WhiteWidgetStyle; typedef void (*CustomWidgetDrawFunction)(struct GWidgetObject *gw, void *param); /** + * @brief Defines a the type of a tag on a widget + */ +typedef uint16_t WidgetTag; + +/** * @brief The structure to initialise a widget. * * @note Some widgets may have extra parameters. @@ -92,6 +97,9 @@ typedef struct GWidgetInit { CustomWidgetDrawFunction customDraw; // @< A custom draw function - use NULL for the standard void * customParam; // @< A parameter for the custom draw function (default = NULL) const GWidgetStyle * customStyle; // @< A custom style to use - use NULL for the default style + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + WidgetTag tag; // @< The tag to associate with the widget + #endif } GWidgetInit; /** @} */ @@ -110,6 +118,9 @@ typedef struct GWidgetObject { CustomWidgetDrawFunction fnDraw; // @< The current draw function void * fnParam; // @< A parameter for the current draw function const GWidgetStyle * pstyle; // @< The current widget style colors + #if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + WidgetTag tag; // @< The widget tag + #endif } GWidgetObject; /** @} */ @@ -187,6 +198,34 @@ void gwinSetText(GHandle gh, const char *text, bool_t useAlloc); */ const char *gwinGetText(GHandle gh); +#if GWIN_WIDGET_TAGS || defined(__DOXYGEN__) + /** + * @brief Set the tag of a widget. + * + * @param[in] gh The widget handle + * @param[in] tag The tag to set. + * + * @note Non-widgets will ignore this call. + * + * @pre Requires GWIN_WIDGET_TAGS to be TRUE + * + * @api + */ + void gwinSetTag(GHandle gh, WidgetTag tag); + + /** + * @brief Get the tag of a widget. + * @return The widget tag value (or 0 if it is not a widget) + * + * @param[in] gh The widget handle + * + * @pre Requires GWIN_WIDGET_TAGS to be TRUE + * + * @api + */ + WidgetTag gwinGetTag(GHandle gh); +#endif + /** * @brief Set the style of a widget. * @@ -235,7 +274,7 @@ void gwinSetCustomDraw(GHandle gh, CustomWidgetDrawFunction fn, void *param); */ bool_t gwinAttachListener(GListener *pl); -#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE +#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__) /** * @brief Set the mouse to be used to control the widgets * @return TRUE on success @@ -249,7 +288,7 @@ bool_t gwinAttachListener(GListener *pl); bool_t gwinAttachMouse(uint16_t instance); #endif -#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE +#if (GFX_USE_GINPUT && GINPUT_NEED_TOGGLE) || defined(__DOXYGEN__) /** * @brief Attach a toggle to a widget * @return TRUE on success @@ -267,7 +306,7 @@ bool_t gwinAttachListener(GListener *pl); bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance); #endif -#if GFX_USE_GINPUT && GINPUT_NEED_DIAL +#if (GFX_USE_GINPUT && GINPUT_NEED_DIAL) || defined(__DOXYGEN__) /** * @brief Attach a toggle to a widget * @return TRUE on success diff --git a/src/gwin/list.c b/src/gwin/list.c index c2a857e3..98ec2ed5 100644 --- a/src/gwin/list.c +++ b/src/gwin/list.c @@ -66,6 +66,9 @@ static void sendListEvent(GWidgetObject *gw, int item) { ple->type = GEVENT_GWIN_LIST; ple->list = (GHandle)gw; ple->item = item; + #if GWIN_WIDGET_TAGS + ple->tag = gw->tag; + #endif geventSendEvent(psl); } diff --git a/src/gwin/list.h b/src/gwin/list.h index 9e31bf2a..1eae3c19 100644 --- a/src/gwin/list.h +++ b/src/gwin/list.h @@ -40,6 +40,9 @@ typedef struct GEventGWinList { GEventType type; // The type of this event (GEVENT_GWIN_LIST) GHandle list; // The list int item; // The item that has been selected (or unselected in a multi-select listbox) + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The list tag + #endif } GEventGWinList; // A list window diff --git a/src/gwin/radio.c b/src/gwin/radio.c index af7b877d..557061e4 100644 --- a/src/gwin/radio.c +++ b/src/gwin/radio.c @@ -38,6 +38,9 @@ static void SendRadioEvent(GWidgetObject *gw) { pbe->type = GEVENT_GWIN_RADIO; pbe->radio = (GHandle)gw; pbe->group = ((GRadioObject *)gw)->group; + #if GWIN_WIDGET_TAGS + pbe->tag = gw->tag; + #endif geventSendEvent(psl); } diff --git a/src/gwin/radio.h b/src/gwin/radio.h index 196f8e27..eb7ee719 100644 --- a/src/gwin/radio.h +++ b/src/gwin/radio.h @@ -37,6 +37,9 @@ typedef struct GEventGWinRadio { GEventType type; // The type of this event (GEVENT_GWIN_RADIO) GHandle radio; // The radio button that has been depressed uint16_t group; // The group for this radio button + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The radio tag + #endif } GEventGWinRadio; /** diff --git a/src/gwin/slider.c b/src/gwin/slider.c index b488f823..7ce7b83f 100644 --- a/src/gwin/slider.c +++ b/src/gwin/slider.c @@ -38,6 +38,9 @@ static void SendSliderEvent(GWidgetObject *gw) { pse->type = GEVENT_GWIN_SLIDER; pse->slider = (GHandle)gw; pse->position = ((GSliderObject *)gw)->pos; + #if GWIN_WIDGET_TAGS + pse->tag = gw->tag; + #endif geventSendEvent(psl); } diff --git a/src/gwin/slider.h b/src/gwin/slider.h index 41244186..32161d62 100644 --- a/src/gwin/slider.h +++ b/src/gwin/slider.h @@ -30,6 +30,9 @@ typedef struct GEventGWinSlider { GEventType type; // The type of this event (GEVENT_GWIN_BUTTON) GHandle slider; // The slider that is returning results int position; + #if GWIN_WIDGET_TAGS + WidgetTag tag; // The slider tag + #endif } GEventGWinSlider; // There are currently no GEventGWinSlider listening flags - use 0 diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h index b1b58a68..d5240556 100644 --- a/src/gwin/sys_options.h +++ b/src/gwin/sys_options.h @@ -128,6 +128,15 @@ * @{ */ /** + * @brief Add a tag to each widget + * @details Defaults to FALSE + * @note Adds a tag member to each widget. Any events created include this tag. + * The enables switch based application logic to detect the event source. + */ + #ifndef GWIN_WIDGET_TAGS + #define GWIN_WIDGET_TAGS FALSE + #endif + /** * @brief Use flat styling for controls rather than a 3D look * @details Defaults to FALSE * @note This may appear better on color-restricted displays |