diff options
65 files changed, 2283 insertions, 863 deletions
diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index 641625e6..783ed548 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -194,7 +194,7 @@ static void createWidgets(void) { wi.g.width = ScrWidth/2 - 2*border; ghConsole = gwinConsoleCreate(0, &wi.g); gwinSetColor(ghConsole, Black); - gwinSetBgColor(ghConsole, 0xF0F0F0); + gwinSetBgColor(ghConsole, HTML2COLOR(0xF0F0F0)); // Buttons wi.g.parent = ghPgButtons; diff --git a/docs/releases.txt b/docs/releases.txt index 2d5bb648..2908535e 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -15,6 +15,15 @@ FEATURE: Added support for eCos FEATURE: Added PCF8812 gdisp driver FEATURE: Added PCD8544 gdisp driver FEATURE: Added Raspberry Pi board support +FEATURE: Added R61505U gdisp driver +FIX: Fix threading issues in GEvent for callbacks +FEATURE: Added geventEventComplete() +FEATURE: Added support for the RawOS real time operating system +FEATURE: Operating System initialisation is now optional +FEATURE: Added optional transparency to container +FEATURE: Prevent mouse events going to obscured widgets +FEATURE: Add GFILE support for file lists +FEATURE: Add GFILE support for C strings as files *** Release 2.1 *** diff --git a/drivers/gdisp/R61505U/board_R61505U_template.h b/drivers/gdisp/R61505U/board_R61505U_template.h new file mode 100644 index 00000000..a4787730 --- /dev/null +++ b/drivers/gdisp/R61505U/board_R61505U_template.h @@ -0,0 +1,60 @@ +/* + * 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 + */ + +#ifndef GDISP_LLD_BOARD_H +#define GDISP_LLD_BOARD_H + +static inline void init_board(GDisplay *g) { + (void) g; +} + +static inline void post_init_board(GDisplay *g) { + (void) g; +} + +static inline void setpin_reset(GDisplay *g, bool_t state) { + (void) g; + (void) state; +} + +static inline void set_backlight(GDisplay *g, uint8_t percent) { + (void) g; + (void) percent; +} + +static inline void acquire_bus(GDisplay *g) { + (void) g; +} + +static inline void release_bus(GDisplay *g) { + (void) g; +} + +static inline void write_index(GDisplay *g, uint16_t index) { + (void) g; + (void) index; +} + +static inline void write_data(GDisplay *g, uint16_t data) { + (void) g; + (void) data; +} + +static inline void setreadmode(GDisplay *g) { + (void) g; +} + +static inline void setwritemode(GDisplay *g) { + (void) g; +} + +static inline uint16_t read_data(GDisplay *g) { + (void) g; + return 0; +} + +#endif /* GDISP_LLD_BOARD_H */ diff --git a/drivers/gdisp/R61505U/gdisp_lld.mk b/drivers/gdisp/R61505U/gdisp_lld.mk new file mode 100644 index 00000000..ff0d0bf4 --- /dev/null +++ b/drivers/gdisp/R61505U/gdisp_lld.mk @@ -0,0 +1,2 @@ +GFXINC += $(GFXLIB)/drivers/gdisp/R61505U +GFXSRC += $(GFXLIB)/drivers/gdisp/R61505U/gdisp_lld_R61505U.c diff --git a/drivers/gdisp/R61505U/gdisp_lld_R61505U.c b/drivers/gdisp/R61505U/gdisp_lld_R61505U.c new file mode 100644 index 00000000..dd966c03 --- /dev/null +++ b/drivers/gdisp/R61505U/gdisp_lld_R61505U.c @@ -0,0 +1,385 @@ +/* + * 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 + */ + +#include "gfx.h" + +#if GFX_USE_GDISP + +/* This controller is only ever used with a 240 x 320 display */ +#if defined(GDISP_SCREEN_HEIGHT) + #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored." + #undef GDISP_SCREEN_HEIGHT +#endif +#if defined(GDISP_SCREEN_WIDTH) + #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored." + #undef GDISP_SCREEN_WIDTH +#endif + +#define GDISP_DRIVER_VMT GDISPVMT_R61505U +#include "drivers/gdisp/R61505U/gdisp_lld_config.h" +#include "src/gdisp/driver.h" + +#include "board_R61505U.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#ifndef GDISP_SCREEN_HEIGHT + #define GDISP_SCREEN_HEIGHT 320 +#endif +#ifndef GDISP_SCREEN_WIDTH + #define GDISP_SCREEN_WIDTH 240 +#endif +#ifndef GDISP_INITIAL_CONTRAST + #define GDISP_INITIAL_CONTRAST 50 +#endif +#ifndef GDISP_INITIAL_BACKLIGHT + #define GDISP_INITIAL_BACKLIGHT 100 +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#define dummy_read(g) { volatile uint16_t dummy; dummy = read_data(g); (void) dummy; } +#define write_reg(g, reg, data) { write_index(g, reg); write_data(g, data); } + +static void set_cursor(GDisplay *g) { + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + write_reg(g, 0x20, g->p.x); + write_reg(g, 0x21, g->p.y); + break; + + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + write_reg(g, 0x20, g->p.y); + write_reg(g, 0x21, g->p.x); + break; + } + write_index(g, 0x22); +} + +static void set_viewport(GDisplay *g) { + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + write_reg(g, 0x50, g->p.x); + write_reg(g, 0x51, g->p.x + g->p.cx - 1); + write_reg(g, 0x52, g->p.y); + write_reg(g, 0x53, g->p.y + g->p.cy - 1); + break; + + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + write_reg(g, 0x50, g->p.y); + write_reg(g, 0x51, g->p.y + g->p.cy - 1); + write_reg(g, 0x52, g->p.x); + write_reg(g, 0x53, g->p.x + g->p.cx - 1); + break; + } +} + +LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { + uint16_t cver; + + // No private area for this controller + g->priv = 0; + + // Initialise the board interface + init_board(g); + + /* Hardware reset */ + setpin_reset(g, TRUE); + gfxSleepMicroseconds(1000); + setpin_reset(g, FALSE); + gfxSleepMicroseconds(1000); + + acquire_bus(g); + write_index(g, 0); // Get controller version + setreadmode(g); + dummy_read(g); + cver = read_data(g); + setwritemode(g); + + /* initializing funciton */ + write_reg(g, 0xe5,0x8000); /* Set the internal vcore voltage */ + write_reg(g, 0x00,0x0001); /* start OSC */ + write_reg(g, 0x2b,0x0010); /* Set the frame rate as 80 when the internal resistor is used for oscillator circuit */ + write_reg(g, 0x01,0x0100); /* s720 to s1 ; G1 to G320 */ + write_reg(g, 0x02,0x0700); /* set the line inversion */ + write_reg(g, 0x03,0x1018); /* 65536 colors */ + write_reg(g, 0x04,0x0000); + write_reg(g, 0x08,0x0202); /* specify the line number of front and back porch periods respectively */ + write_reg(g, 0x09,0x0000); + write_reg(g, 0x0a,0x0000); + write_reg(g, 0x0c,0x0000); /* select internal system clock */ + write_reg(g, 0x0d,0x0000); + write_reg(g, 0x0f,0x0000); + write_reg(g, 0x50,0x0000); /* set windows adress */ + write_reg(g, 0x51,0x00ef); + write_reg(g, 0x52,0x0000); + write_reg(g, 0x53,0x013f); + write_reg(g, 0x60,0x2700); + write_reg(g, 0x61,0x0001); + write_reg(g, 0x6a,0x0000); + write_reg(g, 0x80,0x0000); + write_reg(g, 0x81,0x0000); + write_reg(g, 0x82,0x0000); + write_reg(g, 0x83,0x0000); + write_reg(g, 0x84,0x0000); + write_reg(g, 0x85,0x0000); + write_reg(g, 0x90,0x0010); + write_reg(g, 0x92,0x0000); + write_reg(g, 0x93,0x0003); + write_reg(g, 0x95,0x0110); + write_reg(g, 0x97,0x0000); + write_reg(g, 0x98,0x0000); + /* power setting function */ + write_reg(g, 0x10,0x0000); + write_reg(g, 0x11,0x0000); + write_reg(g, 0x12,0x0000); + write_reg(g, 0x13,0x0000); + gfxSleepMicroseconds(100); + write_reg(g, 0x10,0x17b0); + write_reg(g, 0x11,0x0004); + gfxSleepMicroseconds(50); + write_reg(g, 0x12,0x013e); + gfxSleepMicroseconds(50); + write_reg(g, 0x13,0x1f00); + write_reg(g, 0x29,0x000f); + gfxSleepMicroseconds(50); + write_reg(g, 0x20,0x0000); + write_reg(g, 0x21,0x0000); + + /* initializing function */ + write_reg(g, 0x30,0x0204); + write_reg(g, 0x31,0x0001); + write_reg(g, 0x32,0x0000); + write_reg(g, 0x35,0x0206); + write_reg(g, 0x36,0x0600); + write_reg(g, 0x37,0x0500); + write_reg(g, 0x38,0x0505); + write_reg(g, 0x39,0x0407); + write_reg(g, 0x3c,0x0500); + write_reg(g, 0x3d,0x0503); + + /* display on */ + write_reg(g, 0x07,0x0173); + + gfxSleepMicroseconds(50); + + // Finish Init + post_init_board(g); + + // Release the bus + release_bus(g); + + // Turn on the backlight + set_backlight(g, GDISP_INITIAL_BACKLIGHT); + + /* Initialise the GDISP structure */ + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Orientation = GDISP_ROTATE_0; + g->g.Powermode = powerOn; + g->g.Backlight = GDISP_INITIAL_BACKLIGHT; + g->g.Contrast = GDISP_INITIAL_CONTRAST; + return TRUE; +} + +#if GDISP_HARDWARE_STREAM_WRITE + LLDSPEC void gdisp_lld_write_start(GDisplay *g) { + acquire_bus(g); + set_viewport(g); + #if !GDISP_HARDWARE_STREAM_POS + set_cursor(g); + #endif + } + LLDSPEC void gdisp_lld_write_color(GDisplay *g) { + write_data(g, gdispColor2Native(g->p.color)); + } + LLDSPEC void gdisp_lld_write_stop(GDisplay *g) { + release_bus(g); + } + #if GDISP_HARDWARE_STREAM_POS + LLDSPEC void gdisp_lld_write_pos(GDisplay *g) { + set_cursor(g); + } + #endif +#endif + +#if GDISP_HARDWARE_STREAM_READ + LLDSPEC void gdisp_lld_read_start(GDisplay *g) { + acquire_bus(g); + set_viewport(g); + set_cursor(g); + setreadmode(g); + dummy_read(g); + } + LLDSPEC color_t gdisp_lld_read_color(GDisplay *g) { + uint16_t data; + + data = read_data(g); + return gdispNative2Color(data); + } + LLDSPEC void gdisp_lld_read_stop(GDisplay *g) { + setwritemode(g); + release_bus(g); + } +#endif + +#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + LLDSPEC void gdisp_lld_control(GDisplay *g) { + switch(g->p.x) { + case GDISP_CONTROL_POWER: + if (g->g.Powermode == (powermode_t)g->p.ptr) + return; + switch((powermode_t)g->p.ptr) { + case powerOff: + acquire_bus(g); + write_reg(g, 0x07, 0x0000); + write_reg(g, 0x10, 0x0000); + write_reg(g, 0x11, 0x0000); + write_reg(g, 0x12, 0x0000); + write_reg(g, 0x13, 0x0000); + release_bus(g); + + set_backlight(g, 0); + break; + + case powerOn: + //*************Power On sequence ******************// + acquire_bus(g); + write_reg(g, 0x10, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */ + write_reg(g, 0x11, 0x0000); /* DC1[2:0], DC0[2:0], VC[2:0] */ + write_reg(g, 0x12, 0x0000); /* VREG1OUT voltage */ + write_reg(g, 0x13, 0x0000); /* VDV[4:0] for VCOM amplitude */ + gfxSleepMicroseconds(2000); /* Dis-charge capacitor power voltage */ + write_reg(g, 0x10, 0x17B0); /* SAP, BT[3:0], AP, DSTB, SLP, STB */ + write_reg(g, 0x11, 0x0147); /* DC1[2:0], DC0[2:0], VC[2:0] */ + gfxSleepMicroseconds(500); + write_reg(g, 0x12, 0x013C); /* VREG1OUT voltage */ + gfxSleepMicroseconds(500); + write_reg(g, 0x13, 0x0E00); /* VDV[4:0] for VCOM amplitude */ + write_reg(g, 0x29, 0x0009); /* VCM[4:0] for VCOMH */ + gfxSleepMicroseconds(500); + write_reg(g, 0x07, 0x0173); /* 262K color and display ON */ + release_bus(g); + + set_backlight(g, g->g.Backlight); + break; + + case powerSleep: + acquire_bus(g); + write_reg(g, 0x07, 0x0000); /* display OFF */ + write_reg(g, 0x10, 0x0000); /* SAP, BT[3:0], APE, AP, DSTB, SLP */ + write_reg(g, 0x11, 0x0000); /* DC1[2:0], DC0[2:0], VC[2:0] */ + write_reg(g, 0x12, 0x0000); /* VREG1OUT voltage */ + write_reg(g, 0x13, 0x0000); /* VDV[4:0] for VCOM amplitude */ + gfxSleepMicroseconds(2000); /* Dis-charge capacitor power voltage */ + write_reg(g, 0x10, 0x0002); /* SAP, BT[3:0], APE, AP, DSTB, SLP */ + release_bus(g); + + set_backlight(g, 0); + break; + + case powerDeepSleep: + acquire_bus(g); + write_reg(g, 0x07, 0x0000); /* display OFF */ + write_reg(g, 0x10, 0x0000); /* SAP, BT[3:0], APE, AP, DSTB, SLP */ + write_reg(g, 0x11, 0x0000); /* DC1[2:0], DC0[2:0], VC[2:0] */ + write_reg(g, 0x12, 0x0000); /* VREG1OUT voltage */ + write_reg(g, 0x13, 0x0000); /* VDV[4:0] for VCOM amplitude */ + gfxSleepMicroseconds(2000); /* Dis-charge capacitor power voltage */ + write_reg(g, 0x10, 0x0004); /* SAP, BT[3:0], APE, AP, DSTB, SLP */ + release_bus(g); + + set_backlight(g, 0); + break; + + default: + return; + } + g->g.Powermode = (powermode_t)g->p.ptr; + return; + + case GDISP_CONTROL_ORIENTATION: + if (g->g.Orientation == (orientation_t)g->p.ptr) + return; + switch((orientation_t)g->p.ptr) { + case GDISP_ROTATE_0: + acquire_bus(g); + write_reg(g, 0x01, 0x0100); + write_reg(g, 0x03, 0x1038); + write_reg(g, 0x60, 0x2700); + release_bus(g); + + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Width = GDISP_SCREEN_WIDTH; + break; + + case GDISP_ROTATE_90: + acquire_bus(g); + write_reg(g, 0x01, 0x0000); + write_reg(g, 0x03, 0x1030); + write_reg(g, 0x60, 0x2700); + release_bus(g); + + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + + case GDISP_ROTATE_180: + acquire_bus(g); + write_reg(g, 0x01, 0x0000); + write_reg(g, 0x03, 0x1030); + write_reg(g, 0x60, 0x2700); + release_bus(g); + + g->g.Height = GDISP_SCREEN_HEIGHT; + g->g.Width = GDISP_SCREEN_WIDTH; + break; + + case GDISP_ROTATE_270: + acquire_bus(g); + write_reg(g, 0x01, 0x0100); + write_reg(g, 0x03, 0x1038); + write_reg(g, 0x60, 0xA700); + release_bus(g); + + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + + default: + return; + } + g->g.Orientation = (orientation_t)g->p.ptr; + return; + + case GDISP_CONTROL_BACKLIGHT: + if ((unsigned)g->p.ptr > 100) + g->p.ptr = (void *)100; + set_backlight(g, (unsigned)g->p.ptr); + g->g.Backlight = (unsigned)g->p.ptr; + return; + default: + return; + } + } +#endif + +#endif /* GFX_USE_GDISP */ diff --git a/drivers/gdisp/R61505U/gdisp_lld_config.h b/drivers/gdisp/R61505U/gdisp_lld_config.h new file mode 100644 index 00000000..04834079 --- /dev/null +++ b/drivers/gdisp/R61505U/gdisp_lld_config.h @@ -0,0 +1,26 @@ +/* + * 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 + */ + +#ifndef GDISP_LLD_CONFIG_H +#define GDISP_LLD_CONFIG_H + +#if GFX_USE_GDISP + +/*===========================================================================*/ +/* Driver hardware support. */ +/*===========================================================================*/ + +#define GDISP_HARDWARE_STREAM_WRITE TRUE +#define GDISP_HARDWARE_STREAM_READ TRUE +//#define GDISP_HARDWARE_STREAM_POS TRUE +#define GDISP_HARDWARE_CONTROL TRUE + +#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565 + +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_LLD_CONFIG_H */ diff --git a/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c b/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c index 74fa3e3e..f465b47d 100644 --- a/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c +++ b/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c @@ -16,6 +16,7 @@ #define CALC_PERIOD(w,b,f,p) (p+b+w+f) #define CALC_FPR(w,h,hb,hf,hp,vb,vf,vp,fps) ((fps * CALC_PERIOD(w,hb,hf,hp) * CALC_PERIOD(h,vb,vf,vp) * 1048576)/100000000) + typedef struct LCD_Parameters { coord_t width, height; // Panel width and height uint16_t hbporch; // Horizontal Back Porch @@ -27,6 +28,26 @@ typedef struct LCD_Parameters { uint16_t vpulse; // Vertical Pulse uint16_t vperiod; // Vertical Period (Total) uint32_t fpr; // Calculated FPR + uint16_t flags; // For command "SSD1963_SET_GDISP_MODE" + /* Set the pannel data width */ + #define LCD_PANEL_DATA_WIDTH_24BIT (1<<5) // 18bit default + /* Set the color deeph enhancement */ + #define LCD_PANEL_ENABLE_FRC ((1<<3) | (1<<4)) + #define LCD_PANEL_ENABLE_DITHERING (1<<4) // no enhancement default + /* Set the dot clock pulse polarity */ + #define LCD_PANEL_LSHIFT_FALLING_EDGE (1<<2) // default rising edge + /* Set the horizontal sync pulse polarity */ + #define LCD_PANEL_LLINE_ACTIVE_HIGH (1<<1) // default active low + /* Set the vertical sync pulse polarity */ + #define LCD_PANEL_LFRAME_ACTIVE_HIGH (1<0) // default active low + /* Set the lcd panel mode */ + #define LCD_PANEL_MODE_TTL ((1<<7) << 8) // default mode is Hsync+Vsync +DE + /* Set the lcd panel interface type */ // default TFT mode + #define LCD_PANEL_TYPE_SERIAL_RGB_MODE ((1<<6) << 8) // Serial RGB mode + #define LCD_PANEL_TYPE_SERIAL_RGB_DUMMY_MODE (((1<<5) | (1<<6)) << 8) // Serial RGB+dummy mode + + + } LCD_Parameters; #include "board_SSD1963.h" @@ -48,17 +69,55 @@ typedef struct LCD_Parameters { #include "drivers/gdisp/SSD1963/ssd1963.h" +#define dummy_read(g) { volatile uint16_t dummy; dummy = read_data(g); (void) dummy; } #define write_reg(g, reg, data) { write_index(g, reg); write_data(g, data); } #define write_data16(g, data) { write_data(g, (data)>>8); write_data(g, (data) & 0xFF); } static inline void set_viewport(GDisplay* g) { - write_index(g, SSD1963_SET_PAGE_ADDRESS); - write_data16(g, g->p.y); - write_data16(g, g->p.y+g->p.cy-1); - write_index(g, SSD1963_SET_COLUMN_ADDRESS); - write_data16(g, g->p.x); - write_data16(g, g->p.x+g->p.cx-1); - write_index(g, SSD1963_WRITE_MEMORY_START); + + switch(g->g.Orientation) { + default: + case GDISP_ROTATE_0: + write_index(g, SSD1963_SET_COLUMN_ADDRESS); + write_data16(g, g->p.x); + write_data16(g, g->p.x+g->p.cx-1); + write_index(g, SSD1963_SET_PAGE_ADDRESS); + write_data16(g, g->p.y); + write_data16(g, g->p.y+g->p.cy-1); + write_index(g, SSD1963_WRITE_MEMORY_START); + break; + case GDISP_ROTATE_90: + write_index(g, SSD1963_SET_COLUMN_ADDRESS); + write_data16(g, g->p.y); + write_data16(g, g->p.y+g->p.cy-1); + write_index(g, SSD1963_SET_PAGE_ADDRESS); + write_data16(g, GDISP_SCREEN_HEIGHT-1 - (g->p.x+g->p.cx-1) ); + write_data16(g, GDISP_SCREEN_HEIGHT-1 - (g->p.x)); + write_index(g, SSD1963_WRITE_MEMORY_START); + break; + case GDISP_ROTATE_180: + write_index(g, SSD1963_SET_COLUMN_ADDRESS); + write_data16(g, GDISP_SCREEN_WIDTH-1 - (g->p.x+g->p.cx-1)); + write_data16(g, GDISP_SCREEN_WIDTH-1 - (g->p.x)); + write_index(g, SSD1963_SET_PAGE_ADDRESS); + write_data16(g, GDISP_SCREEN_HEIGHT-1 - (g->p.y+g->p.cy-1)); + write_data16(g, GDISP_SCREEN_HEIGHT-1 - (g->p.y)); + write_index(g, SSD1963_WRITE_MEMORY_START); + break; + case GDISP_ROTATE_270: + write_index(g, SSD1963_SET_COLUMN_ADDRESS); + write_data16(g, GDISP_SCREEN_WIDTH-1 - (g->p.y+g->p.cy-1)); + write_data16(g, GDISP_SCREEN_WIDTH-1 - (g->p.y)); + write_index(g, SSD1963_SET_PAGE_ADDRESS); + write_data16(g, g->p.x); + write_data16(g, g->p.x+g->p.cx-1); + write_index(g, SSD1963_WRITE_MEMORY_START); + break; + } + + + + } /** @@ -71,12 +130,18 @@ static inline void set_backlight(GDisplay *g, uint8_t percent) { //Check your LCD's hardware, the PWM connection is default left open and instead //connected to a LED connection on the breakout board write_index(g, SSD1963_SET_PWM_CONF); //set PWM for BackLight - write_data(g, 0x01); - write_data(g, (55+percent*2) & 0x00FF); - write_data(g, 0x01); //controlled by host (not DBC), enabled - write_data(g, 0xFF); - write_data(g, 0x60); //don't let it go too dark, avoid a useless LCD - write_data(g, 0x0F); //prescaler ??? + write_data(g, 4); // PWMF[7:0] = 4, + // PWM signal frequency = PLL clock / (256 * (PWMF[7:0] + 1)) / 256 + // = aprox 366Hz for a PLL freq = 120MHz + if ( percent==0xFF ) // use percent==0xFF to turn off SSD1963 pwm in power SLEEP or DEEP SLEEP mode + write_data(g, 0x00); + else + write_data(g, (55+percent*2) & 0x00FF); + write_data(g, 0x01); // controlled by host (not DBC), enabled + write_data(g, 0x00); // DBC manual brightness; Set the brightness level: 00 Dimmest, FF Brightest + write_data(g, 0x00); // DBC minimum brightness; Set the minimum brightness level: 00 Dimmest, FF Brightest + write_data(g, 0x00); // Brightness prescaler of Transition Effect; Set the brightness prescaler: 0 Dimmest, F Brightest + } /*===========================================================================*/ @@ -121,10 +186,10 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { write_index(g, SSD1963_SOFT_RESET); gfxSleepMilliseconds(5); - /* Screen size */ + /* LCD panel parameters */ write_index(g, SSD1963_SET_GDISP_MODE); - write_data(g, 0x18); //Enabled dithering - write_data(g, 0x00); + write_data(g, lcdp->flags & 0xFF); + write_data(g, (lcdp->flags >> 8) & 0xFF); write_data16(g, lcdp->width-1); write_data16(g, lcdp->height-1); write_data(g, 0x00); // RGB @@ -155,9 +220,10 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { /* Tear effect indicator ON. This is used to tell the host MCU when the driver is not refreshing the panel (during front/back porch) */ write_reg(g, SSD1963_SET_TEAR_ON, 0x00); + /* Turn on */ write_index(g, SSD1963_SET_DISPLAY_ON); - + /* Turn on the back-light */ set_backlight(g, GDISP_INITIAL_BACKLIGHT); @@ -190,8 +256,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { } #endif -// Not implemented yet. -#if 0 && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL +#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL LLDSPEC void gdisp_lld_control(GDisplay *g) { switch(g->p.x) { case GDISP_CONTROL_POWER: @@ -200,23 +265,34 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { switch((powermode_t)g->p.ptr) { case powerOff: acquire_bus(g); - write_index(g, SSD1963_EXIT_SLEEP_MODE); // leave sleep mode - gfxSleepMilliseconds(5); - write_index(g, SSD1963_SET_DISPLAY_OFF); - write_index(g, SSD1963_SET_DEEP_SLEEP); // enter deep sleep mode release_bus(g); break; case powerOn: acquire_bus(g); - read_reg(0x0000); gfxSleepMilliseconds(5); // 2x Dummy reads to wake up from deep sleep - read_reg(0x0000); gfxSleepMilliseconds(5); - write_index(g, SSD1963_SET_DISPLAY_ON); + dummy_read(g); + dummy_read(g); + /* Wait for 1msec to let the PLL stable if was stopped by deep sleep mode */ + gfxSleepMilliseconds(100); + write_index(g, SSD1963_EXIT_SLEEP_MODE); + gfxSleepMilliseconds(5); + /* Restore the back-light */ + set_backlight(g, gdispGGetBacklight(g)); release_bus(g); break; case powerSleep: acquire_bus(g); - write_index(g, SSD1963_SET_DISPLAY_OFF); - write_index(g, SSD1963_ENTER_SLEEP_MODE); // enter sleep mode + /* Turn off the back-light pwm from SSD1963 */ + set_backlight(g, 0xFF); + write_index(g, SSD1963_ENTER_SLEEP_MODE); + gfxSleepMilliseconds(5); + release_bus(g); + break; + case powerDeepSleep: + acquire_bus(g); + /* Turn off the back-light pwm from SSD1963 */ + set_backlight(g, 0xFF); + write_index(g, SSD1963_ENTER_SLEEP_MODE); + write_index(g, SSD1963_SET_DEEP_SLEEP); release_bus(g); break; default: @@ -228,30 +304,35 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { case GDISP_CONTROL_ORIENTATION: if (g->g.Orientation == (orientation_t)g->p.ptr) return; + switch((orientation_t)g->p.ptr) { case GDISP_ROTATE_0: acquire_bus(g); - /* Code here */ + write_index(g, SSD1963_SET_ADDRESS_MODE); + write_data(g, 0x00); release_bus(g); g->g.Height = ((LCD_Parameters *)g->priv)->height; g->g.Width = ((LCD_Parameters *)g->priv)->width; break; case GDISP_ROTATE_90: acquire_bus(g); - /* Code here */ + write_index(g, SSD1963_SET_ADDRESS_MODE); + write_data(g, SSD1963_ADDR_MODE_PAGE_ADDR_ORDER | SSD1963_ADDR_MODE_PAG_COL_ADDR_ORDER ); release_bus(g); g->g.Height = ((LCD_Parameters *)g->priv)->width; g->g.Width = ((LCD_Parameters *)g->priv)->height; break; case GDISP_ROTATE_180: acquire_bus(g); - /* Code here */ + write_index(g, SSD1963_SET_ADDRESS_MODE); + write_data(g, SSD1963_ADDR_MODE_PAGE_ADDR_ORDER | SSD1963_ADDR_MODE_COL_ADDR_ORDER); release_bus(g); g->g.Height = ((LCD_Parameters *)g->priv)->height; g->g.Width = ((LCD_Parameters *)g->priv)->width; break; case GDISP_ROTATE_270: acquire_bus(g); - /* Code here */ + write_index(g, SSD1963_SET_ADDRESS_MODE); + write_data(g, SSD1963_ADDR_MODE_COL_ADDR_ORDER | SSD1963_ADDR_MODE_PAG_COL_ADDR_ORDER ); release_bus(g); g->g.Height = ((LCD_Parameters *)g->priv)->width; g->g.Width = ((LCD_Parameters *)g->priv)->height; @@ -259,7 +340,7 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { default: return; } - g->g.Orientation = (orientation_t)g->p.ptr; + g->g.Orientation = (orientation_t)g->p.ptr; return; case GDISP_CONTROL_BACKLIGHT: diff --git a/drivers/gdisp/SSD1963/gdisp_lld_config.h b/drivers/gdisp/SSD1963/gdisp_lld_config.h index f1b61702..7dfd0627 100644 --- a/drivers/gdisp/SSD1963/gdisp_lld_config.h +++ b/drivers/gdisp/SSD1963/gdisp_lld_config.h @@ -15,7 +15,7 @@ /*===========================================================================*/ #define GDISP_HARDWARE_STREAM_WRITE TRUE -//#define GDISP_HARDWARE_CONTROL TRUE // Not Yet. +#define GDISP_HARDWARE_CONTROL TRUE #define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565 diff --git a/drivers/gdisp/SSD1963/ssd1963.h b/drivers/gdisp/SSD1963/ssd1963.h index 2d1ac55f..eec2ae99 100644 --- a/drivers/gdisp/SSD1963/ssd1963.h +++ b/drivers/gdisp/SSD1963/ssd1963.h @@ -93,4 +93,13 @@ #define SSD1963_PDI_9BIT 6 #define SSD1963_GET_PIXEL_DATA_INTERFACE 0x00F1 +#define SSD1963_ADDR_MODE_FLIP_VERT (1 << 0) +#define SSD1963_ADDR_MODE_FLIP_HORZ (1 << 1) +#define SSD1963_ADDR_MODE_LATCH_RIGHT_TO_LEFT (1 << 2) +#define SSD1963_ADDR_MODE_BGR (1 << 3) +#define SSD1963_ADDR_MODE_REFRESH_BOTTOM_UP (1 << 4) +#define SSD1963_ADDR_MODE_PAG_COL_ADDR_ORDER (1 << 5) +#define SSD1963_ADDR_MODE_COL_ADDR_ORDER (1 << 6) +#define SSD1963_ADDR_MODE_PAGE_ADDR_ORDER (1 << 7) + #endif diff --git a/drivers/multiple/Win32/readme.txt b/drivers/multiple/Win32/readme.txt index 353f5a5b..932deb8b 100644 --- a/drivers/multiple/Win32/readme.txt +++ b/drivers/multiple/Win32/readme.txt @@ -16,7 +16,14 @@ optionally a touchscreen driver, and optionally a toggle driver. 2. To your makefile add the following lines:
include $(GFXLIB)/gfx.mk
- include $(GFXLIB)/drivers/multiple/Win32/gdisp_lld.mk
+ include $(GFXLIB)/drivers/multiple/Win32/driver.mk
+
+ However, consider using the Win32 board file instead as this does include all
+ the possible drivers that can be used (eg. for the GAUDIO module) by using:
+
+ include $(GFXLIB)/gfx.mk
+ include $(GFXLIB)/boards/base/Win32/board.mk
+
3. Modify your makefile to add -lws2_32 and -lgdi32 to the DLIBS line. i.e.
DLIBS = -lws2_32 -lgdi32
diff --git a/gfxconf.example.h b/gfxconf.example.h index ef6bedbe..2abcf0b4 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -38,6 +38,9 @@ // #define INTERRUPTS_OFF() optional_code // #define INTERRUPTS_ON() optional_code +// Options that (should where relevant) apply to all operating systems +// #define GFX_NO_OS_INIT FALSE + /////////////////////////////////////////////////////////////////////////// // GDISP // @@ -166,6 +169,7 @@ // #define GWIN_NEED_PROGRESSBAR FALSE // #define GWIN_PROGRESSBAR_AUTO FALSE // #define GWIN_FLAT_STYLING FALSE +// #define GWIN_WIDGET_TAGS FALSE //#define GWIN_NEED_CONTAINERS FALSE // #define GWIN_NEED_CONTAINER FALSE @@ -220,10 +224,8 @@ //#define GFILE_NEED_PRINTG FALSE //#define GFILE_NEED_SCANG FALSE //#define GFILE_NEED_STRINGS FALSE +//#define GFILE_NEED_FILELISTS FALSE //#define GFILE_NEED_STDIO FALSE -// #define GFILE_ALLOW_FLOATS FALSE -// #define GFILE_ALLOW_DEVICESPECIFIC FALSE -// #define GFILE_MAX_GFILES 3 //#define GFILE_NEED_NOAUTOMOUNT FALSE //#define GFILE_NEED_NOAUTOSYNC FALSE @@ -234,6 +236,9 @@ //#define GFILE_NEED_NATIVEFS FALSE //#define GFILE_NEED_CHBIOSFS FALSE +//#define GFILE_ALLOW_FLOATS FALSE +//#define GFILE_ALLOW_DEVICESPECIFIC FALSE +//#define GFILE_MAX_GFILES 3 /////////////////////////////////////////////////////////////////////////// // GADC // diff --git a/license.html b/license.html index f6d39eb7..953e4bbf 100644 --- a/license.html +++ b/license.html @@ -2,7 +2,7 @@ <!-- saved from url=(0028)http://ugfx.org/license.html --> <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="utf-8"> - <title>GFX License, version 1.1</title> + <title>GFX License, version 1.2</title> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> @@ -67,7 +67,7 @@ </style> <style type="text/css"></style></head> <body> -<h1>GFX License<br>Version 1.1</h1> +<h1>GFX License<br>Version 1.2</h1> <h2>1. Definitions</h2> <dl> <dt>1.1. Works</dt> @@ -100,7 +100,7 @@ <dt>1.12. Commercial Use</dt> <dd><p>means any use of the Works that may result in income or equivalent benefit in any form. Examples of Commercial Use include; selling an Application or Device containing the Works, offering paid support to modify the Works, selling a library containing the Works. Examples of use that are NOT Commercial Use include; using the Works for Education, use of the Works in hobbyist projects that have no expectation of income, use of the Works in building what will later become a Commercial Use product. - Note that Commercial Use does not the prototyping and preparation for income generating activities - only the income producing activity itself.</p></dd> + Note that Commercial Use does not include the prototyping and preparation for income generating activities - only the income producing activity itself.</p></dd> </dl> <h2>2. Non Commercial Use</h2> @@ -116,7 +116,7 @@ <h3>2.2. Distribution of Executable form</h3> <p>If You distribute the Works in an Executable form then:</p> <ol type="a"> -<li><p>and You must inform recipients of the Executable form that it contains the Works and how they can obtain a copy of the Works in Source Code Form from the License Owners master repository; and</p></li> +<li><p>You must inform recipients of the Executable form that it contains the Works and how they can obtain a copy of the Works in Source Code Form from the License Owners master repository; and</p></li> <li><p>Any modification to the Works must be contributed to the License Owners as per Section 4 prior to distribution; and</p></li> <li><p>You may distribute such Executable form under the terms of this License, or license it under different terms, provided that the license for the Executable form does not allow for Commercial Use of the Works or attempt to limit or alter the recipients' rights in the Source Code Form under this License.</p></li> </ol> @@ -125,7 +125,7 @@ <h3>3.1. Commercial Use Agreement</h3> <p>A "Commercial Use Agreement" explicitly signed by the License Owner will override any specified terms of this License for the party to the agreement under the terms of the agreement. All other terms of this License will still apply.</p> <h3>3.2. Distribution</h3> -<p>Other than as provided for in a signed "Commercial Use Agreement", where there is a Commercial Use involved or implied; you are not permitted to distribute the Works, in any form, either in source code or Executable form, +<p>Other than as provided for in a signed "Commercial Use Agreement", where there is a Commercial Use involved or implied, you are not permitted to distribute the Works in any form, either in source code or Executable form, either as software or on a device or in any other way.</p> <h2>4. Contributions</h2> @@ -177,7 +177,8 @@ <p>You may distribute the Works under the terms of the version of the License under which You originally received the Works, or under the terms of any subsequent version published by the license steward.</p> <h3>12.3. Modified Versions</h3> <p>If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the License Owner (except to note that such modified license differs from this License).</p> - +<h3>13. Source Code License Notice</h3> +<p>Where ever possible the source code for the Works is to contain a header as per Exhibit A. Where this would be syntactically incorrect then modifications to allow syntactic parsing by a compiler are allowed but only to the extent that they allow correct syntactic parsing. If it is not possible to put the notice in a particular file, then You may include the notice in a location where a recipient would be likely to look for such a notice such as a “license.txt” file in a relevant directory.</p> <h2>Exhibit A - Source Code Form License Notice</h2> <pre>/* * This file is subject to the terms of the GFX License. If a copy of @@ -186,7 +187,5 @@ * http://ugfx.com/license.html */ </pre> -<p>If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.</p> - </body></html> 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 |