diff --git a/drivers/multiple/Win32/gdisp_lld.c b/drivers/multiple/Win32/gdisp_lld.c index 221442ef..7364300a 100644 --- a/drivers/multiple/Win32/gdisp_lld.c +++ b/drivers/multiple/Win32/gdisp_lld.c @@ -24,6 +24,15 @@ #include #include +/* Our threading model - ChibiOS or Win32 */ +#ifndef GDISP_THREAD_CHIBIOS + #if GFX_USE_OS_WIN32 + #define GDISP_THREAD_CHIBIOS FALSE + #else + #define GDISP_THREAD_CHIBIOS TRUE + #endif +#endif + #ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH 640 #endif @@ -230,10 +239,7 @@ static LRESULT myWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) return 0; } -static DWORD WINAPI WindowThread(LPVOID lpParameter) { - (void)lpParameter; - - MSG msg; +static void InitWindow(void) { HANDLE hInstance; WNDCLASS wc; RECT rect; @@ -274,15 +280,40 @@ static DWORD WINAPI WindowThread(LPVOID lpParameter) { ShowWindow(winRootWindow, SW_SHOW); UpdateWindow(winRootWindow); isReady = TRUE; - - while(GetMessage(&msg, NULL, 0, 0) > 0) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - ExitProcess(0); - return msg.wParam; } +#if GDISP_THREAD_CHIBIOS + static DECLARESTACK(waWindowThread, 1024); + static threadreturn_t WindowThread(void *param) { + (void)param; + MSG msg; + + InitWindow(); + do { + gfxSleepMilliseconds(1); + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } while (msg.message != WM_QUIT); + ExitProcess(0); + return msg.wParam; + } +#else + static DWORD WINAPI WindowThread(LPVOID param) { + (void)param; + MSG msg; + + InitWindow(); + while(GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + ExitProcess(0); + return msg.wParam; + } +#endif + /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ @@ -312,7 +343,11 @@ bool_t gdisp_lld_init(void) { wHeight = GDISP_SCREEN_HEIGHT; /* Initialise the window */ +#if GDISP_THREAD_CHIBIOS + gfxCreateThread(waWindowThread, sizeof(waWindowThread), HIGH_PRIORITY, WindowThread, 0); +#else CreateThread(0, 0, WindowThread, 0, 0, 0); +#endif while (!isReady) Sleep(1); diff --git a/gfx.mk b/gfx.mk index c476e096..7cd19d28 100644 --- a/gfx.mk +++ b/gfx.mk @@ -7,6 +7,7 @@ GFXINC += $(GFXLIB)/include GFXSRC += $(GFXLIB)/src/gfx.c include $(GFXLIB)/src/gos/gos.mk +include $(GFXLIB)/src/gqueue/gqueue.mk include $(GFXLIB)/src/gdisp/gdisp.mk include $(GFXLIB)/src/tdisp/tdisp.mk include $(GFXLIB)/src/gevent/gevent.mk diff --git a/gfxconf.example.h b/gfxconf.example.h index 5ba739a9..48e39b31 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -25,6 +25,7 @@ #define GFX_USE_GWIN FALSE #define GFX_USE_GEVENT FALSE #define GFX_USE_GTIMER FALSE +#define GFX_USE_GQUEUE FALSE #define GFX_USE_GINPUT FALSE #define GFX_USE_GADC FALSE #define GFX_USE_GAUDIN FALSE @@ -90,6 +91,11 @@ /* Features for the GTIMER subsystem. */ /* NONE */ +/* Features for the GQUEUE subsystem. */ +#define GQUEUE_NEED_ASYNC FALSE +#define GQUEUE_NEED_GSYNC FALSE +#define GQUEUE_NEED_FSYNC FALSE + /* Features for the GINPUT subsystem. */ #define GINPUT_NEED_MOUSE FALSE #define GINPUT_NEED_KEYBOARD FALSE @@ -118,6 +124,8 @@ #define GTIMER_THREAD_WORKAREA_SIZE 512 #define GADC_MAX_LOWSPEED_DEVICES 4 #define GWIN_BUTTON_LAZY_RELEASE FALSE + #define GWIN_CONSOLE_USE_BASESTREAM FALSE + #define GWIN_CONSOLE_USE_FLOAT FALSE */ /* Optional Low Level Driver Definitions */ diff --git a/include/gfx.h b/include/gfx.h index 84aec39f..ac9a5382 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -86,6 +86,14 @@ #ifndef GFX_USE_GTIMER #define GFX_USE_GTIMER FALSE #endif + /** + * @brief GFX Queue API + * @details Defaults to FALSE + * @details Provides queue management. + */ + #ifndef GFX_USE_GQUEUE + #define GFX_USE_GQUEUE FALSE + #endif /** * @brief GFX Input Device API * @details Defaults to FALSE @@ -143,6 +151,7 @@ */ #include "gos/options.h" #include "gmisc/options.h" +#include "gqueue/options.h" #include "gevent/options.h" #include "gtimer/options.h" #include "gdisp/options.h" @@ -164,6 +173,7 @@ */ #include "gos/gos.h" #include "gmisc/gmisc.h" +#include "gqueue/gqueue.h" #include "gevent/gevent.h" #include "gtimer/gtimer.h" #include "gdisp/gdisp.h" diff --git a/include/gfx_rules.h b/include/gfx_rules.h index 4fff2d21..4b7e9506 100644 --- a/include/gfx_rules.h +++ b/include/gfx_rules.h @@ -101,6 +101,15 @@ #if GDISP_NEED_MULTITHREAD && GDISP_NEED_ASYNC #error "GDISP: Only one of GDISP_NEED_MULTITHREAD and GDISP_NEED_ASYNC should be defined." #endif + #if GDISP_NEED_ASYNC && !(GFX_USE_GQUEUE && GQUEUE_NEED_GSYNC) + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GFX_USE_GQUEUE or GQUEUE_NEED_GSYNC is not TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GQUEUE + #define GFX_USE_GQUEUE TRUE + #undef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC TRUE + #endif #endif #if GFX_USE_TDISP @@ -141,6 +150,9 @@ #if GFX_USE_GAUDOUT #endif +#if GFX_USE_GQUEUE +#endif + #if GFX_USE_GMISC #endif diff --git a/include/gos/chibios.h b/include/gos/chibios.h index 37bf93ee..1fa93141 100644 --- a/include/gos/chibios.h +++ b/include/gos/chibios.h @@ -67,17 +67,6 @@ typedef struct { #define gfxMutex Mutex -typedef struct gfxQueue { - struct gfxQueueItem *head; - struct gfxQueueItem *tail; - Semaphore sem; - } gfxQueue; - -typedef struct gfxQueueItem { - struct gfxQueueItem *next; - Semaphore sem; - } gfxQueueItem; - /*===========================================================================*/ /* Function declarations. */ /*===========================================================================*/ @@ -90,30 +79,24 @@ extern "C" { #define gfxExit() chSysHalt() #define gfxAlloc(sz) chHeapAlloc(NULL, sz) #define gfxFree(ptr) chHeapFree(ptr) +#define gfxYield() chThdYield() +#define gfxSystemTicks() chTimeNow() +#define gfxMillisecondsToTicks(ms) MS2ST(ms) +#define gfxSystemLock() chSysLock() +#define gfxSystemUnlock() chSysUnlock() +#define gfxMutexInit(pmutex) chMtxInit(pmutex) +#define gfxMutexDestroy(pmutex) ; +#define gfxMutexEnter(pmutex) chMtxLock(pmutex) +#define gfxMutexExit(pmutex) chMtxUnlock() void gfxSleepMilliseconds(delaytime_t ms); void gfxSleepMicroseconds(delaytime_t ms); void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); +void gfxSemDestroy(gfxSem *psem); bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); void gfxSemSignal(gfxSem *psem); void gfxSemSignalI(gfxSem *psem); #define gfxSemCounterI(psem) ((psem)->sem.s_cnt) #define gfxSemCounter(psem) ((psem)->sem.s_cnt) -#define gfxSystemTicks() chTimeNow() -#define gfxMillisecondsToTicks(ms) MS2ST(ms) -#define gfxYield() chThdYield() -#define gfxSystemLock() chSysLock() -#define gfxSystemUnlock() chSysUnlock() -#define gfxMutexInit(pmutex) chMtxInit(pmutex) -#define gfxMutexEnter(pmutex) chMtxLock(pmutex) -#define gfxMutexExit(pmutex) chMtxUnlock() -void gfxQueueInit(gfxQueue *pqueue); -gfxQueueItem * gfxQueueGet(gfxQueue *pqueue, delaytime_t ms); -bool_t gfxQueuePut(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms); -#define gfxQueuePop(q) gfxQueueGet(q) -bool_t gfxQueuePush(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms); -void gfxQueueRemove(gfxQueue *pqueue, gfxQueueItem *pitem); -bool_t gfxQueueIsEmpty(gfxQueue *pqueue); -bool_t gfxQueueIsIn(gfxQueue *pqueue, gfxQueueItem *pitem); bool_t gfxCreateThread(void *stackarea, size_t stacksz, threadpriority_t prio, gfxThreadFunction fn, void *param); #ifdef __cplusplus diff --git a/include/gos/gos.h b/include/gos/gos.h index 04799540..d979cd27 100644 --- a/include/gos/gos.h +++ b/include/gos/gos.h @@ -36,11 +36,6 @@ /* Type definitions */ /*===========================================================================*/ - /** - * @brief A function for a new thread to execute. - */ - typedef threadreturn_t (*gfxThreadFunction)(void *param); - /** * @brief Various integer sizes * @note Your platform may use slightly different definitions to these @@ -66,6 +61,10 @@ typedef short semcount_t; typedef int threadreturn_t; typedef int threadpriority_t; + /** + * @brief A function for a new thread to execute. + */ + typedef threadreturn_t (*gfxThreadFunction)(void *param); /** * @} * @@ -77,7 +76,7 @@ #define TRUE 1 #define TIME_IMMEDIATE 0 #define TIME_INFINITE ((delaytime_t)-1) - #define MAX_SEMAPHORE_COUNT ((semcount_t)-1) + #define MAX_SEMAPHORE_COUNT ((semcount_t)(((unsigned long)((semcount_t)(-1))) >> 1)) #define LOW_PRIORITY 0 #define NORMAL_PRIORITY 1 #define HIGH_PRIORITY 2 @@ -96,18 +95,6 @@ */ typedef struct {} gfxMutex; - /** - * @brief A queue - * @note Your operating system will have a proper definition for this structure - */ - typedef struct {} gfxQueue; - - /** - * @brief A queue item - * @note Your operating system will have a proper definition for this structure - */ - typedef struct {} gfxQueueItem; - /*===========================================================================*/ /* Function declarations. */ /*===========================================================================*/ @@ -151,6 +138,15 @@ */ void gfxFree(void *ptr); + /** + * @brief Yield the current thread + * @details Give up the rest of the current time slice for this thread in order to give other threads + * a chance to run. + * + * @api + */ + void gfxYield(void); + /** * @brief Put the current thread to sleep for the specified period in milliseconds * @@ -176,6 +172,99 @@ */ void gfxSleepMicroseconds(delaytime_t ms); + /** + * @brief Get the current operating system tick time + * @return The current tick time + * + * @note A "tick" is an arbitrary period of time that the operating + * system uses to mark time. + * @note The absolute value of this call is relatively meaningless. Its usefulness + * is in calculating periods between two calls to this function. + * @note As the value from this function can wrap it is important that any periods are calculated + * as t2 - t1 and then compared to the desired period rather than comparing + * t1 + period to t2 + * + * @api + */ + systemticks_t gfxSystemTicks(void); + + /** + * @brief Convert a given number of millseconds to a number of operating system ticks + * @return The period in system ticks. + * + * @note A "tick" is an arbitrary period of time that the operating + * system uses to mark time. + * + * @param[in] ms The number of millseconds + * + * @api + */ + systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + + /** + * @brief Lock the operating system to protect a sequence of code + * + * @note Calling this will lock out all other threads from executing even at interrupt level + * within the GFX system. On hardware this may be implemented as a disabling of interrupts, + * however in an operating system which hides real interrupt level code it may simply use a + * mutex lock. + * @note The thread MUST NOT block whilst the system is locked. It must execute in this state for + * as short a period as possible as this can seriously affect interrupt latency on some + * platforms. + * @note While locked only interrupt level (iclass) GFX routines may be called. + * + * @api + */ + void gfxSystemLock(void); + + /** + * @brief Unlock the operating system previous locked by gfxSystemLock() + * + * @api + */ + void gfxSystemUnlock(void); + + /** + * @brief Initialise a mutex to protect a region of code from other threads. + * + * @param[in] pmutex A pointer to the mutex + * + * @note Whilst a counting semaphore with a limit of 1 can be used for similiar purposes + * on many operating systems using a seperate mutex structure is more efficient. + * + * @api + */ + void gfxMutexInit(gfxMutex *pmutex); + + /** + * @brief Destroy a Mutex. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexDestroy(gfxMutex *pmutex); + + /** + * @brief Enter the critical code region protected by the mutex. + * @details Blocks until there is no other thread in the critical region. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexEnter(gfxMutex *pmutex); + + /** + * @brief Exit the critical code region protected by the mutex. + * @details May cause another thread waiting on the mutex to now be placed into the run queue. + * + * @param[in] pmutex A pointer to the mutex + * + * @api + */ + void gfxMutexExit(gfxMutex *pmutex); + /** * @brief Initialise a Counted Semaphore * @@ -194,6 +283,17 @@ */ void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); + /** + * @brief Destroy a Counted Semaphore + * + * @param[in] psem A pointer to the semaphore + * + * @note Any threads waiting on the semaphore will be released + * + * @api + */ + void gfxSemDestroy(gfxSem *psem); + /** * @brief Wait on a semaphore * @details The semaphore counter is decreased and if the result becomes negative the thread waits for it to become @@ -253,199 +353,6 @@ */ semcount_t gfxSemCounterI(gfxSem *pSem); - /** - * @brief Get the current operating system tick time - * @return The current tick time - * - * @note A "tick" is an arbitrary period of time that the operating - * system uses to mark time. - * @note The absolute value of this call is relatively meaningless. Its usefulness - * is in calculating periods between two calls to this function. - * @note As the value from this function can wrap it is important that any periods are calculated - * as t2 - t1 and then compared to the desired period rather than comparing - * t1 + period to t2 - * - * @api - */ - systemticks_t gfxSystemTicks(void); - - /** - * @brief Convert a given number of millseconds to a number of operating system ticks - * @return The period in system ticks. - * - * @note A "tick" is an arbitrary period of time that the operating - * system uses to mark time. - * - * @param[in] ms The number of millseconds - * - * @api - */ - systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - - /** - * @brief Yield the current thread - * @details Give up the rest of the current time slice for this thread in order to give other threads - * a chance to run. - * - * @api - */ - void gfxYield(void); - - /** - * @brief Lock the operating system to protect a sequence of code - * - * @note Calling this will lock out all other threads from executing even at interrupt level - * within the GFX system. On hardware this may be implemented as a disabling of interrupts, - * however in an operating system which hides real interrupt level code it may simply use a - * mutex lock. - * @note The thread MUST NOT block whilst the system is locked. It must execute in this state for - * as short a period as possible as this can seriously affect interrupt latency on some - * platforms. - * @note While locked only interrupt level (iclass) GFX routines may be called. - * - * @api - */ - void gfxSystemLock(void); - - /** - * @brief Unlock the operating system previous locked by gfxSystemLock() - * - * @api - */ - void gfxSystemUnlock(void); - - /** - * @brief Initialise a mutex to protect a region of code from other threads. - * - * @param[in] pmutex A pointer to the mutex - * - * @note Whilst a counting semaphore with a limit of 1 can be used for similiar purposes - * on many operating systems using a seperate mutex structure is more efficient. - * - * @api - */ - void gfxMutexInit(gfxMutex *pmutex); - - /** - * @brief Enter the critical code region protected by the mutex. - * @details Blocks until there is no other thread in the critical region. - * - * @param[in] pmutex A pointer to the mutex - * - * @api - */ - void gfxMutexEnter(gfxMutex *pmutex); - - /** - * @brief Exit the critical code region protected by the mutex. - * @details May cause another thread waiting on the mutex to now be placed into the run queue. - * - * @param[in] pmutex A pointer to the mutex - * - * @api - */ - void gfxMutexExit(gfxMutex *pmutex); - - /** - * @brief Initialise a queue. - * - * @param[in] pqueue A pointer to the queue - * - * @note Whilst queues are normally FIFO, a GFX queue also supports push and pop operations. - * A pop operation is the same as normal get from the queue but a push places the item - * at the head of the queue instead of the tail (as a put would). - * - * @api - */ - void gfxQueueInit(gfxQueue *pqueue); - - /** - * @brief Get an item from the head of the queue. - * @return NULL if the timeout expires before an item is available - * - * @param[in] pqueue A pointer to the queue - * @param[in] ms The maxmimum time to wait for an item - * - * @api - */ - gfxQueueItem * gfxQueueGet(gfxQueue *pqueue, delaytime_t ms); - - /** - * @brief Put an item on the end of the queue. - * @return FALSE on timeout, otherwise TRUE - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * @param[in] ms The maxmimum time to wait for an item to be removed from the queue - * - * @note Use a delay time of TIME_IMMEDIATE if you don't want to wait until the - * item is removed from the queue - * - * @api - */ - bool_t gfxQueuePut(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms); - - /** - * @brief Pop an item from the head of the queue. - * @return NULL if there are no more items on the queue - * - * @param[in] pqueue A pointer to the queue - * - * @api - */ - #define gfxQueuePop(pqueue, ms) gfxQueueGet(pqueue, ms) - - /** - * @brief Push an item into the start of the queue. - * @return FALSE on timeout, otherwise TRUE - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * @param[in] ms The maxmimum time to wait for an item to be popped - * - * @note Use a delay time of TIME_IMMEDIATE if you don't want to wait until the - * item is removed from the queue - * - * @api - */ - bool_t gfxQueuePush(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms); - - /** - * @brief Remove an item from the queue. - * @note Removes the specified item from the queue whereever it is in the queue - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * - * @note If the item isn't in the queue the routine just returns. - * - * @api - */ - void gfxQueueRemove(gfxQueue *pqueue, gfxQueueItem *pitem); - - /** - * @brief Is the queue empty? - * @return TRUE if the queue is empty - * - * @param[in] pqueue A pointer to the queue - * - * @api - */ - bool_t gfxQueueIsEmpty(gfxQueue *pqueue); - - /** - * @brief Is an item in the queue? - * @return TRUE if the item is in the queue? - * - * @param[in] pqueue A pointer to the queue - * @param[in] pitem A pointer to the queue item - * - * @note This operation may be expensive. - * - * @api - */ - bool_t gfxQueueIsIn(gfxQueue *pqueue, gfxQueueItem *pitem); - /** * @brief Start a new thread. * @return Return TRUE if the thread was started, FALSE on an error diff --git a/include/gos/win32.h b/include/gos/win32.h index a6f28865..80a1f430 100644 --- a/include/gos/win32.h +++ b/include/gos/win32.h @@ -26,6 +26,80 @@ #define _GOS_WIN32_H #if GFX_USE_OS_WIN32 + +//#define WIN32_LEAN_AND_MEAN +#include + +/** + * size_t + * TRUE, FALSE + * are already defined by Win32 + */ +typedef __int8 bool_t; +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef DWORD delaytime_t; +typedef DWORD systemticks_t; +typedef LONG semcount_t; +#define threadreturn_t DWORD WINAPI +typedef int threadpriority_t; + +typedef threadreturn_t (*gfxThreadFunction)(void *param); + +#define TIME_IMMEDIATE 0 +#define TIME_INFINITE INFINITE +#define MAX_SEMAPHORE_COUNT ((semcount_t)(((unsigned long)((semcount_t)(-1))) >> 1)) +#define LOW_PRIORITY THREAD_PRIORITY_BELOW_NORMAL +#define NORMAL_PRIORITY THREAD_PRIORITY_NORMAL +#define HIGH_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL +#define DECLARESTACK(name, sz) uint8_t name[0]; + +typedef HANDLE gfxSem; +typedef HANDLE gfxMutex; + +#define gfxExit() ExitProcess(0) +#define gfxAlloc(sz) malloc(sz) +#define gfxFree(ptr) free(ptr) +#define gfxSleepMilliseconds(ms) Sleep(ms) +#define gfxYield() Sleep(0) +#define gfxSystemTicks() GetTickCount() +#define gfxMillisecondsToTicks(ms) (ms) +#define gfxMutexInit(pmutex) *(pmutex) = CreateMutex(NULL, FALSE, NULL) +#define gfxMutexDestory(pmutex) CloseHandle(*(pmutex)) +#define gfxMutexEnter(pmutex) WaitForSingleObject(*(pmutex), INFINITE) +#define gfxMutexExit(pmutex) ReleaseMutex(*(pmutex)) +#define gfxSemInit(psem, val, limit) *(psem) = CreateSemaphore(NULL, val, limit, NULL) +#define gfxSemDestory(psem) CloseHandle(*(psem)) +#define gfxSemSignal(psem) ReleaseSemaphore(*(psem), 1, NULL) +#define gfxSemSignalI(psem) ReleaseSemaphore(*(psem), 1, NULL) +#define gfxSemCounterI(psem) gfxSemCounter(psem) + +/*===========================================================================*/ +/* Function declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void gfxHalt(const char *msg); +void gfxSleepMicroseconds(delaytime_t ms); +bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); +semcount_t gfxSemCounter(gfxSem *pSem); +void gfxSystemLock(void); +void gfxSystemUnlock(void); +bool_t gfxCreateThread(void *stackarea, size_t stacksz, threadpriority_t prio, gfxThreadFunction fn, void *param); + +#ifdef __cplusplus +} +#endif + + + #endif /* GFX_USE_OS_WIN32 */ #endif /* _GOS_WIN32_H */ diff --git a/include/gqueue/gqueue.h b/include/gqueue/gqueue.h new file mode 100644 index 00000000..deeca93b --- /dev/null +++ b/include/gqueue/gqueue.h @@ -0,0 +1,229 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * @file include/gqueue/gqueue.h + * @brief GQUEUE header file. + * + * @addtogroup GQUEUE + * @brief GQUEUE provides queue management. There are 3 types of queues: + *
  • Asynchronous Queues (ASync) - Queue operations never block
  • + *
  • Get Synchronous Queues (GSync) - Queue Get operations block until something is placed in the Queue
  • + *
  • Put Synchronous Queues (PSync) - Queue Put operations block until the element is removed from the Queue
  • + *
  • Fully Synchronous Queues (FSync) - Queue GET and Put operations block
  • + *
+ * We need 4 types of queues even though fully synchronous queues support all operations including asynchronous + * operations because fully synchronous queues have the highest storage requirements. The other queue types are + * optimizations. Efficiency IS important to use (particularly RAM efficiency). + * In practice we only implement ASync, GSync and FSync queues as PSync queues are of dubious value. + * @{ + */ + +#ifndef _GQUEUE_H +#define _GQUEUE_H + +#if GFX_USE_GQUEUE || defined(__DOXYGEN__) + +/** + * @brief A queue + * @{ + */ +typedef struct gfxQueueASync { + struct gfxQueueASyncItem *head; + struct gfxQueueASyncItem *tail; + } gfxQueueAsync; +typedef struct gfxQueueGSync { + struct gfxQueueGSyncItem *head; + struct gfxQueueGSyncItem *tail; + gfxSem sem; + } gfxQueueGSync; +typedef struct gfxQueueFSync { + struct gfxQueueFSyncItem *head; + struct gfxQueueFSyncItem *tail; + gfxSem sem; + } gfxQueueGSync; +/* @} */ + +/** + * @brief A queue item + * @{ + */ +typedef struct gfxQueueASyncItem { + struct gfxQueueASyncItem *next; + } gfxQueueASyncItem; +typedef struct gfxQueueGSyncItem { + struct gfxQueueGSyncItem *next; + } gfxQueueGSyncItem; +typedef struct gfxQueueFSyncItem { + struct gfxQueueFSyncItem *next; + gfxSem sem; + } gfxQueueFSyncItem; +/* @} */ + + +/*===========================================================================*/ +/* Function declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise a queue. + * + * @param[in] pqueue A pointer to the queue + * + * @note Whilst queues are normally FIFO, a GFX queue also supports push and pop operations. + * A pop operation is the same as normal get from the queue but a push places the item + * at the head of the queue instead of the tail (as a put would). + * + * @api + * @{ + */ +void gfxQueueASyncInit(gfxQueueASync *pqueue); +void gfxQueueGSyncInit(gfxQueueGSync *pqueue); +void gfxQueueFSyncInit(gfxQueueFSync *pqueue); +/* @} */ + +/** + * @brief Get an item from the head of the queue. + * @return NULL if the timeout expires before an item is available + * + * @param[in] pqueue A pointer to the queue + * @param[in] ms The maxmimum time to wait for an item. For ASync queues this parameter is + * not specified as TIME_IMMEDIATE is assumed. + * + * @api + * @{ + */ +gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue); +gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms); +gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms); +/* @} */ + +/** + * @brief Put an item on the end of the queue. + * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * @param[in] ms The maxmimum time to wait for an item to be removed from the queue (only for FSync queues) + * + * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the + * item is removed from the queue. Note that even if the timeout occurs - the item + * remains in the queue. + * + * @api + * @{ + */ +void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); +/* @} */ + +/** + * @brief Pop an item from the head of the queue. + * @detail This is exactly the same as the Get operation above. + * + * @api + * @{ + */ +#define gfxQueueASyncPop(pqueue) gfxQueueASyncGet(pqueue) +#define gfxQueueGSyncPop(pqueue, ms) gfxQueueGSyncGet(pqueue, ms) +#define gfxQueueFSyncPop(pqueue, ms) gfxQueueFSyncGet(pqueue, ms) +/* @} */ + +/** + * @brief Push an item into the start of the queue. + * @return none for ASync and GSync queues; For FSync queues - FALSE on timeout, otherwise TRUE + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * @param[in] ms The maxmimum time to wait for an item to be popped (only for FSync queues) + * + * @note FSync: Use a delay time of TIME_IMMEDIATE if you don't want to wait until the + * item is removed from the queue. Note that even if the timeout occurs - the item + * remains in the queue. + * + * @api + * @{ + */ +void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms); +/* @} */ + +/** + * @brief Remove an item from the queue. + * @note Removes the specified item from the queue where-ever it is in the queue + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * + * @note If the item isn't in the queue the routine just returns. + * @note If a process is waiting on the Put/Push operation for the item, that process + * will be signaled. + * + * @api + * @{ + */ +void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem); +/* @} */ + +/** + * @brief Is the queue empty? + * @return TRUE if the queue is empty + * + * @param[in] pqueue A pointer to the queue + * + * @api + * @{ + */ +bool_t gfxQueueASyncIsEmpty(gfxQueueASync *pqueue); +bool_t gfxQueueGSyncIsEmpty(gfxQueueGSync *pqueue); +bool_t gfxQueueFSyncIsEmpty(gfxQueueFSync *pqueue); +/* @} */ + +/** + * @brief Is an item in the queue? + * @return TRUE if the item is in the queue? + * + * @param[in] pqueue A pointer to the queue + * @param[in] pitem A pointer to the queue item + * + * @note This operation may be expensive. + * + * @api + * @{ + */ +bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem); +bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem); +bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GQUEUE */ +#endif /* _GQUEUE_H */ +/** @} */ diff --git a/include/gqueue/options.h b/include/gqueue/options.h new file mode 100644 index 00000000..30ccdb46 --- /dev/null +++ b/include/gqueue/options.h @@ -0,0 +1,66 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file include/gqueue/options.h + * @brief GQUEUE - Queue options header file. + * + * @addtogroup GQUEUE + * @{ + */ + +#ifndef _GQUEUE_OPTIONS_H +#define _GQUEUE_OPTIONS_H + +/** + * @name GQUEUE Functions to include. + * @{ + */ + /** + * @brief Enable Asynchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_ASYNC + #define GQUEUE_NEED_ASYNC FALSE + #endif + /** + * @brief Enable Get-Synchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC FALSE + #endif + /** + * @brief Enable Fully Synchronous Queues + * @details Defaults to FALSE + */ + #ifndef GQUEUE_NEED_FSYNC + #define GQUEUE_NEED_FSYNC FALSE + #endif +/** + * @} + * + * @name GQUEUE Optional Sizing Parameters + * @{ + */ +/** @} */ + +#endif /* _GQUEUE_OPTIONS_H */ +/** @} */ diff --git a/include/gwin/console.h b/include/gwin/console.h index 1fc19e42..4c317cdb 100644 --- a/include/gwin/console.h +++ b/include/gwin/console.h @@ -40,7 +40,7 @@ typedef struct GConsoleObject_t { GWindowObject gwin; - #if GFX_USE_OS_CHIBIOS + #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM struct GConsoleWindowStream_t { const struct GConsoleWindowVMT_t *vmt; _base_asynchronous_channel_data @@ -80,7 +80,7 @@ extern "C" { */ GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t width, coord_t height, font_t font); -#if GFX_USE_OS_CHIBIOS +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM /** * @brief Get a stream from a console window suitable for use with chprintf(). * @return The stream handle or NULL if this is not a console window. @@ -128,6 +128,31 @@ void gwinPutString(GHandle gh, const char *str); */ void gwinPutCharArray(GHandle gh, const char *str, size_t n); +/** + * @brief Print a formatted string at the cursor position in the window. It will wrap lines as required. + * @details This function implements a minimal printf() like functionality + * The general parameters format is: %[-][width|*][.precision|*][l|L]p. + * The following parameter types (p) are supported: + * - x hexadecimal integer. + * - X hexadecimal long. + * - o octal integer. + * - O octal long. + * - d decimal signed integer. + * - D decimal signed long. + * - u decimal unsigned integer. + * - U decimal unsigned long. + * - c character. + * - s string. + * @note Uses the current foreground color to draw the string and fills the background using the background drawing color + * + * @param[in] gh The window handle (must be a console window) + * @param[in] fmt The format string (as per printf) + * @param[in] ... The format string arguments. + * + * @api + */ +void gwinPrintf(GHandle gh, const char *fmt, ...); + #ifdef __cplusplus } #endif diff --git a/include/gwin/options.h b/include/gwin/options.h index 8d78a751..683a6e55 100644 --- a/include/gwin/options.h +++ b/include/gwin/options.h @@ -59,9 +59,27 @@ * @name GWIN Optional Parameters * @{ */ + /** + * @brief Buttons should not insist the mouse is over the button on mouse release + * @details Defaults to FALSE + */ #ifndef GWIN_BUTTON_LAZY_RELEASE #define GWIN_BUTTON_LAZY_RELEASE FALSE #endif + /** + * @brief Console Windows need floating point support in @p gwinPrintf + * @details Defaults to FALSE + */ + #ifndef GWIN_CONSOLE_USE_FLOAT + #define GWIN_CONSOLE_USE_FLOAT FALSE + #endif + /** + * @brief Console Windows need BaseStreamSequential support (ChibiOS only) + * @details Defaults to FALSE + */ + #ifndef GWIN_CONSOLE_USE_BASESTREAM + #define GWIN_CONSOLE_USE_BASESTREAM FALSE + #endif /** @} */ #endif /* _GWIN_OPTIONS_H */ diff --git a/src/gos/chibios.c b/src/gos/chibios.c index 6e63a2a4..7e369824 100644 --- a/src/gos/chibios.c +++ b/src/gos/chibios.c @@ -57,13 +57,14 @@ void gfxSleepMicroseconds(delaytime_t ms) { default: chThdSleepMicroseconds(ms); return; } } - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { if (val > limit) val = limit; psem->limit = limit; chSemInit(&psem->sem, val); } - +void gfxSemDestroy(gfxSem *psem) { + chSemReset(&psem->sem, 1); +} bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { if (ms == TIME_INFINITE) { chSemWait(&psem->sem); @@ -85,115 +86,6 @@ void gfxSemSignalI(gfxSem *psem) { chSemSignalI(&psem->sem); } -void gfxQueueInit(gfxQueue *pqueue) { - pqueue->head = pqueue->tail = 0; - chSemInit(&pqueue->sem, 0); -} - -gfxQueueItem * gfxQueueGet(gfxQueue *pqueue, delaytime_t ms) { - gfxQueueItem *pi; - - chSysLock(); - /* If someone else is waiting or if the queue is empty - wait ourselves */ - if (pqueue->sem.s_cnt < 0 || !pqueue->head) { - if (chSemWaitTimeoutS(&pqueue->sem, ms == TIME_INFINITE ? TIME_INFINITE : MS2ST(ms)) == RDY_TIMEOUT) { - chSysUnlock(); - return NULL; - } - } - /* We can now get the head element */ - pi = pqueue->head; - pqueue->head = pi; - chSemSignalI(&pi->sem); - chSysUnlock(); - return pi; -} - -bool_t gfxQueuePut(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms) { - chSemInit(&pitem->sem, 0); - chSysLock(); - pitem->next = 0; - if (!pqueue->head) { - pqueue->head = pqueue->tail = pitem; - } else { - pqueue->tail->next = pitem; - pqueue->tail = pitem; - } - /* Wake up someone who is waiting */ - if (chSemGetCounterI(&pqueue->sem) < 0) - chSemSignalI(&pqueue->sem); - chSysUnlock(); - return chSemWaitTimeout(&pitem->sem, ms == TIME_INFINITE ? TIME_INFINITE : MS2ST(ms)) != RDY_TIMEOUT; -} - -bool_t gfxQueuePush(gfxQueue *pqueue, gfxQueueItem *pitem, delaytime_t ms) { - chSemInit(&pitem->sem, 0); - chSysLock(); - pitem->next = pqueue->head; - pqueue->head = pitem; - if (!pitem->next) - pqueue->tail = pitem; - /* Wake up someone who is waiting */ - if (chSemGetCounterI(&pqueue->sem) < 0) - chSemSignalI(&pqueue->sem); - chSysUnlock(); - return chSemWaitTimeout(&pitem->sem, ms == TIME_INFINITE ? TIME_INFINITE : MS2ST(ms)) != RDY_TIMEOUT; -} - -void gfxQueueRemove(gfxQueue *pqueue, gfxQueueItem *pitem) { - gfxQueueItem *pi; - - chSysLock(); - if (pqueue->head) { - if (pqueue->head == pitem) { - pqueue->head = pitem->next; - chSemSignalI(&pitem->sem); - } else { - for(pi = pqueue->head; pi->next; pi = pi->next) { - if (pi->next == pitem) { - pi->next = pitem->next; - if (pqueue->tail == pitem) - pqueue->tail = pi; - chSemSignalI(&pitem->sem); - break; - } - } - } - } - chSysUnlock(); -} - -bool_t gfxQueueIsEmpty(gfxQueue *pqueue) { - return pqueue->head == NULL; -} - -bool_t gfxQueueIsIn(gfxQueue *pqueue, gfxQueueItem *pitem) { - gfxQueueItem *pi; - - chSysLock(); - for(pi = pqueue->head; pi; pi = pi->next) { - if (pi == pitem) { - chSysUnlock(); - return TRUE; - } - } - chSysUnlock(); - return FALSE; -} - -/** - * @brief Start a new thread. - * @return Return TRUE if the thread was started, FALSE on an error - * - * @param[in] stackarea A pointer to the area for the new threads stack or NULL to dynamically allocate it - * @param[in] stacksz The size of the thread stack. 0 means the default operating system size although this - * is only valid when stackarea is dynamically allocated. - * @param[in] prio The priority of the new thread - * @param[in] fn The function the new thread will run - * @param[in] param A parameter to pass the thread function. - * - * @api - */ bool_t gfxCreateThread(void *stackarea, size_t stacksz, threadpriority_t prio, gfxThreadFunction fn, void *param) { if (!stackarea) { if (!stacksz) stacksz = 256; diff --git a/src/gos/win32.c b/src/gos/win32.c index 6cf803a2..bda57f6f 100644 --- a/src/gos/win32.c +++ b/src/gos/win32.c @@ -26,7 +26,86 @@ #if GFX_USE_OS_WIN32 -#error "GOS: WIN32 not supported yet" +#include + +static HANDLE SystemMutex; + +void _gosInit(void) { +} + +void gfxHalt(const char *msg) { + fprintf(stderr, "%s\n", msg); + ExitProcess(1); +} + +void gfxSleepMicroseconds(delaytime_t ms) { + static LARGE_INTEGER pcfreq; + static int initflag; + LARGE_INTEGER t1, t2, tdiff; + + switch(ms) { + case TIME_IMMEDIATE: return; + case TIME_INFINITE: while(1) Sleep(1000); return; + } + + if (!initflag) { + QueryPerformanceFrequency(&pcfreq); + initflag++; + } + tdiff.QuadPart = pcfreq.QuadPart * ms / 1000000; + + QueryPerformanceCounter(&t1); + do { + QueryPerformanceCounter(&t2); + } while (t2.QuadPart - t1.QuadPart < tdiff.QuadPart); +} + +void gfxSystemLock(void) { + if (!SystemMutex) + SystemMutex = CreateMutex(NULL, FALSE, NULL); + WaitForSingleObject(SystemMutex, INFINITE); +} + +void gfxSystemUnlock(void) { + ReleaseMutex(SystemMutex); +} + +bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { + return WaitForSingleObject(*psem, ms) == WAIT_OBJECT_0; +} + +typedef LONG __stdcall (*_NtQuerySemaphore)( + HANDLE SemaphoreHandle, + DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */ + PVOID SemaphoreInformation, /* but this is to much to dump here */ + ULONG SemaphoreInformationLength, + PULONG ReturnLength OPTIONAL +); + +semcount_t gfxSemCounter(gfxSem *pSem) { + static _NtQuerySemaphore NtQuerySemaphore; + struct _SEMAPHORE_BASIC_INFORMATION { + ULONG CurrentCount; + ULONG MaximumCount; + } BasicInfo; + + if (!NtQuerySemaphore) + NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySemaphore"); + + NtQuerySemaphore(*pSem, 0, &BasicInfo, sizeof(BasicInfo), NULL); + return BasicInfo.CurrentCount; +} + +bool_t gfxCreateThread(void *stackarea, size_t stacksz, threadpriority_t prio, gfxThreadFunction fn, void *param) { + (void) stackarea; + HANDLE thd; + + if (!(thd = CreateThread(NULL, stacksz, fn, param, CREATE_SUSPENDED, NULL))) + return FALSE; + SetThreadPriority(thd, prio); + ResumeThread(thd); + return TRUE; +} #endif /* GFX_USE_OS_WIN32 */ /** @} */ diff --git a/src/gqueue/gqueue.c b/src/gqueue/gqueue.c new file mode 100644 index 00000000..d515a425 --- /dev/null +++ b/src/gqueue/gqueue.c @@ -0,0 +1,255 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * @file src/gqueue/gqueue.c + * @brief GQUEUE source file. + */ +#if GFX_USE_GQUEUE + +#if GQUEUE_NEED_ASYNC + void gfxQueueASyncInit(gfxQueueASync *pqueue) { + pqueue->head = pqueue->tail = 0; + } + gfxQueueASyncItem *gfxQueueASyncGet(gfxQueueASync *pqueue) { + gfxQueueASyncItem *pi; + + if (!pqueue->head) return 0; + gfxSystemLock(); + if ((pi = pqueue->head)) + pqueue->head = pi->next; + gfxSytemUnlock(); + return pi; + } + void gfxQueueASyncPut(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + pitem->next = 0; + + gfxSystemLock(); + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + gfxSystemUnlock(); + } + void gfxQueueASyncPush(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxSystemLock(); + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + gfxSystemUnlock(); + } + void gfxQueueASyncRemove(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + if (!pitem) return; + gfxSystemLock(); + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + } else { + for(gfxQueueASyncItem *pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + break; + } + } + } + } + gfxSystemUnlock(); + } + bool_t gfxQueueASyncIsEmpty(gfxQueueASync *pqueue) { + return pqueue->head == NULL; + } + bool_t gfxQueueASyncIsIn(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) { + gfxSystemLock(); + for(gfxQueueASyncItem *pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) { + gfxSystemUnlock(); + return TRUE; + } + } + gfxSystemUnlock(); + return FALSE; + } +#endif + +#if GQUEUE_NEED_GSYNC + void gfxQueueGSyncInit(gfxQueueGSync *pqueue) { + pqueue->head = pqueue->tail = 0; + gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); + } + gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms) { + gfxQueueGSyncItem *pi; + + if (!gfxSemWait(&pqueue->sem, ms)) return 0; + gfxSystemLock(); + pi = pqueue->head; + pqueue->head = pi->next; + gfxSytemUnlock(); + return pi; + } + void gfxQueueGSyncPut(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + pitem->next = 0; + + gfxSystemLock(); + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + } + void gfxQueueGSyncPush(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxSystemLock(); + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + } + void gfxQueueGSyncRemove(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + if (!pitem) return; + gfxSystemLock(); + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + } else { + for(gfxQueueGSyncItem *pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + break; + } + } + } + } + gfxSystemUnlock(); + } + bool_t gfxQueueGSyncIsEmpty(gfxQueueGSync *pqueue) { + return pqueue->head == NULL; + } + bool_t gfxQueueGSyncIsIn(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) { + gfxSystemLock(); + for(gfxQueueGSyncItem *pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) { + gfxSystemUnlock(); + return TRUE; + } + } + gfxSystemUnlock(); + return FALSE; + } +#endif + +#if GQUEUE_NEED_FSYNC + void gfxQueueFSyncInit(gfxQueueFSync *pqueue) { + pqueue->head = pqueue->tail = 0; + gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT); + } + gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms) { + gfxQueueFSyncItem *pi; + + if (!gfxSemWait(&pqueue->sem, ms)) return 0; + gfxSystemLock(); + pi = pqueue->head; + pqueue->head = pi->next; + gfxSytemUnlock(); + + gfxSemSignalI(&pi->sem); + gfxSemDestroy(&pi->sem); + return pi; + } + bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { + gfxSemInit(&pitem->sem, 0, 1); + pitem->next = 0; + + gfxSystemLock(); + if (!pqueue->head) { + pqueue->head = pqueue->tail = pitem; + } else { + pqueue->tail->next = pitem; + pqueue->tail = pitem; + } + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + return gfxSemWait(&pitem->sem, ms); + } + bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) { + gfxSemInit(&pitem->sem, 0, 1); + + gfxSystemLock(); + pitem->next = pqueue->head; + pqueue->head = pitem; + if (!pitem->next) + pqueue->tail = pitem; + gfxSystemUnlock(); + + gfxSemSignal(&pqueue->sem); + return gfxSemWait(&pitem->sem, ms); + } + void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem) { + if (!pitem) return; + gfxSystemLock(); + if (pqueue->head) { + if (pqueue->head == pitem) { + pqueue->head = pitem->next; + found: + gfxSystemUnlock(); + gfxSemSignal(&pitem->sem); + gfxSemDestroy(&pitem->sem); + return; + } + for(gfxQueueFSyncItem *pi = pqueue->head; pi->next; pi = pi->next) { + if (pi->next == pitem) { + pi->next = pitem->next; + if (pqueue->tail == pitem) + pqueue->tail = pi; + goto found; + } + } + } + gfxSystemUnlock(); + } + bool_t gfxQueueFSyncIsEmpty(gfxQueueFSync *pqueue) { + return pqueue->head == NULL; + } + bool_t gfxQueueFSyncIsIn(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem) { + gfxSystemLock(); + for(gfxQueueFSyncItem *pi = pqueue->head; pi; pi = pi->next) { + if (pi == pitem) { + gfxSystemUnlock(); + return TRUE; + } + } + gfxSystemUnlock(); + return FALSE; + } +#endif + +#endif /* GFX_USE_GQUEUE */ diff --git a/src/gqueue/gqueue.mk b/src/gqueue/gqueue.mk new file mode 100644 index 00000000..ab8a0423 --- /dev/null +++ b/src/gqueue/gqueue.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gqueue/gqueue.c diff --git a/src/gwin/button.c b/src/gwin/button.c index 19301698..cf5babc5 100644 --- a/src/gwin/button.c +++ b/src/gwin/button.c @@ -54,7 +54,7 @@ static void gwinButtonCallback(void *param, GEvent *pe) { #define pbe ((GEventGWinButton *)pe) // check if button is disabled - if (gh->enabled == false) + if (!gh->enabled) return; switch (pe->type) { @@ -150,7 +150,7 @@ GHandle gwinCreateButton(GButtonObject *gb, coord_t x, coord_t y, coord_t width, geventRegisterCallback(&gb->listener, gwinButtonCallback, gb); // buttons are enabled by default - gb->gwin.enabled = true; + gb->gwin.enabled = TRUE; return (GHandle)gb; } diff --git a/src/gwin/console.c b/src/gwin/console.c index a01ed79d..32e4f35a 100644 --- a/src/gwin/console.c +++ b/src/gwin/console.c @@ -30,7 +30,7 @@ * Stream interface implementation. The interface is write only */ -#if GFX_USE_OS_CHIBIOS +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM #define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream)))) static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; } @@ -71,7 +71,7 @@ GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t widt return (GHandle)gc; } -#if GFX_USE_OS_CHIBIOS +#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM BaseSequentialStream *gwinGetConsoleStream(GHandle gh) { if (gh->type != GW_CONSOLE) return 0; @@ -145,6 +145,203 @@ void gwinPutCharArray(GHandle gh, const char *str, size_t n) { gwinPutChar(gh, *str++); } +#include + +#define MAX_FILLER 11 +#define FLOAT_PRECISION 100000 + +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; +} + +#if GWIN_CONSOLE_USE_FLOAT + static char *ftoa(char *p, double num) { + long l; + unsigned long precision = FLOAT_PRECISION; + + l = num; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (num - l) * precision; + return ltoa_wd(p, l, 10, precision / 10); + } +#endif + +void gwinPrintf(GHandle gh, const char *fmt, ...) { + va_list ap; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if CHPRINTF_USE_FLOAT + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + if (gh->type != GW_CONSOLE || !gh->font) return; + + va_start(ap, fmt); + while (TRUE) { + c = *fmt++; + if (c == 0) { + va_end(ap); + return; + } + if (c != '%') { + gwinPutChar(gh, c); + continue; + } + + p = tmpbuf; + s = tmpbuf; + left_align = FALSE; + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + filler = ' '; + if (*fmt == '.') { + fmt++; + filler = '0'; + } + width = 0; + + while (TRUE) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + width = width * 10 + c; + } + precision = 0; + if (c == '.') { + while (TRUE) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + precision = precision * 10 + c; + } + } + /* 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 'c': + filler = ' '; + *p++ = va_arg(ap, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(ap, 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(ap, long); + else + l = va_arg(ap, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if CHPRINTF_USE_FLOAT + case 'f': + f = (float) va_arg(ap, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + p = ftoa(p, f); + 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(ap, long); + else + l = va_arg(ap, 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') { + gwinPutChar(gh, *s++); + i--; + } + do { + gwinPutChar(gh, filler); + } while (++width != 0); + } + while (--i >= 0) + gwinPutChar(gh, *s++); + while (width) { + gwinPutChar(gh, filler); + width--; + } + } +} + #endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */ /** @} */