From 599aecb9d05691c028370be251c3250442dc9d0e Mon Sep 17 00:00:00 2001 From: ekeeke31 Date: Sat, 23 May 2009 17:29:53 +0000 Subject: [PATCH] +added missing image +added throbber support ~fixed texture rotation ~improved wiimote pointing --- source/gx/fileio/dvd.c | 10 ++-- source/gx/fileio/file_dvd.c | 6 +-- source/gx/fileio/file_fat.c | 2 +- source/gx/fileio/file_mem.c | 8 ++-- source/gx/fileio/unzip.c | 2 +- source/gx/gui/filesel.c | 10 ++-- source/gx/gui/gui.c | 32 ++++++++----- source/gx/gui/gui.h | 21 ++++---- source/gx/gui/menu.c | 8 ++-- source/gx/gx_video.c | 71 ++++++++++++++++++++-------- source/gx/gx_video.h | 2 +- source/gx/images/Frame_s4_title.png | Bin 0 -> 2285 bytes source/gx/images/Frame_throbber.png | Bin 0 -> 2334 bytes 13 files changed, 105 insertions(+), 67 deletions(-) create mode 100644 source/gx/images/Frame_s4_title.png create mode 100644 source/gx/images/Frame_throbber.png diff --git a/source/gx/fileio/dvd.c b/source/gx/fileio/dvd.c index ef069c3..3def550 100644 --- a/source/gx/fileio/dvd.c +++ b/source/gx/fileio/dvd.c @@ -63,7 +63,7 @@ u32 dvd_read (void *dst, u32 len, u64 offset) dvd[7] = 3; /*** Enable reading with DMA ***/ - while (dvd[7] & 1); + while (dvd[7] & 1) usleep(10); memcpy (dst, buffer, len); /*** Ensure it has completed ***/ @@ -88,7 +88,7 @@ u32 dvd_read (void *dst, u32 len, u64 offset) ****************************************************************************/ void dvd_motor_off( ) { - GUI_MsgBoxOpen("Information", "Stopping DVD drive ..."); + GUI_MsgBoxOpen("Information", "Stopping DVD drive ...", 1); #ifndef HW_RVL dvd[0] = 0x2e; @@ -99,7 +99,7 @@ void dvd_motor_off( ) dvd[5] = 0; dvd[6] = 0; dvd[7] = 1; // Do immediate - while (dvd[7] & 1); + while (dvd[7] & 1) usleep(10); /*** PSO Stops blackscreen at reload ***/ dvd[0] = 0x14; @@ -133,7 +133,7 @@ void uselessinquiry () dvd[6] = 0x20; dvd[7] = 1; - while (dvd[7] & 1); + while (dvd[7] & 1) usleep(10); } /**************************************************************************** @@ -152,7 +152,7 @@ void dvd_drive_detect() dvd[5] = 0x80000000; dvd[6] = 0x20; dvd[7] = 3; - while( dvd[7] & 1 ); + while( dvd[7] & 1 ) usleep(10); DCFlushRange((void *)0x80000000, 32); int driveid = (int)inquiry[2]; diff --git a/source/gx/fileio/file_dvd.c b/source/gx/fileio/file_dvd.c index 1ec165c..ddb9b9c 100644 --- a/source/gx/fileio/file_dvd.c +++ b/source/gx/fileio/file_dvd.c @@ -302,7 +302,7 @@ int DVD_LoadFile(u8 *buffer) { char msg[50]; sprintf(msg,"Loading %d bytes...", length); - GUI_MsgBoxOpen("Information",msg); + GUI_MsgBoxOpen("Information",msg,1); /* How many 2k blocks to read */ int blocks = length / 2048; int readoffset = 0; @@ -351,7 +351,7 @@ int DVD_Open(void) if (!getpvd()) { /* mount DVD */ - GUI_MsgBoxOpen("Information", "Mounting DVD ... Wait"); + GUI_MsgBoxOpen("Information", "Mounting DVD ...",1); #ifdef HW_RVL u32 val; @@ -364,7 +364,7 @@ int DVD_Open(void) } DI_Mount(); - while(DI_GetStatus() & DVD_INIT); + while(DI_GetStatus() & DVD_INIT) usleep(10); if (!(DI_GetStatus() & DVD_READY)) { char msg[50]; diff --git a/source/gx/fileio/file_fat.c b/source/gx/fileio/file_fat.c index e1719e1..cbd83ef 100644 --- a/source/gx/fileio/file_fat.c +++ b/source/gx/fileio/file_fat.c @@ -196,7 +196,7 @@ int FAT_LoadFile(u8 *buffer) { char msg[50]; sprintf(msg,"Loading %d bytes ...", length); - GUI_MsgBoxOpen("Information",msg); + GUI_MsgBoxOpen("Information",msg,1); fread(buffer, 1, length, sdfile); fclose(sdfile); return length; diff --git a/source/gx/fileio/file_mem.c b/source/gx/fileio/file_mem.c index 280c72e..b5689f1 100644 --- a/source/gx/fileio/file_mem.c +++ b/source/gx/fileio/file_mem.c @@ -232,8 +232,8 @@ int ManageSRAM (u8 direction, u8 device) char filename[MAXJOLIET]; - if (direction) GUI_MsgBoxOpen("Information","Loading SRAM ..."); - else GUI_MsgBoxOpen("Information","Saving SRAM ..."); + if (direction) GUI_MsgBoxOpen("Information","Loading SRAM ...",1); + else GUI_MsgBoxOpen("Information","Saving SRAM ...",1); /* clean buffer */ memset(savebuffer, 0, STATE_SIZE); @@ -432,8 +432,8 @@ int ManageState (u8 direction, u8 device) char filename[MAXJOLIET]; - if (direction) GUI_MsgBoxOpen("Information","Loading State ..."); - else GUI_MsgBoxOpen("Information","Saving State ..."); + if (direction) GUI_MsgBoxOpen("Information","Loading State ...",1); + else GUI_MsgBoxOpen("Information","Saving State ...",1); /* clean buffer */ memset(savebuffer, 0, STATE_SIZE); diff --git a/source/gx/fileio/unzip.c b/source/gx/fileio/unzip.c index c1a77dc..098bda1 100644 --- a/source/gx/fileio/unzip.c +++ b/source/gx/fileio/unzip.c @@ -131,7 +131,7 @@ int UnZipBuffer (unsigned char *outbuffer, u64 discoffset, char *filename) memcpy (&pkzip, &readbuffer, sizeof (PKZIPHEADER)); sprintf (msg, "Unzipping %d bytes ...", FLIP32 (pkzip.uncompressedSize)); - GUI_MsgBoxOpen("Information",msg); + GUI_MsgBoxOpen("Information",msg,1); /*** Prepare the zip stream ***/ memset (&zs, 0, sizeof (z_stream)); diff --git a/source/gx/gui/filesel.c b/source/gx/gui/filesel.c index abfd7fe..0588876 100644 --- a/source/gx/gui/filesel.c +++ b/source/gx/gui/filesel.c @@ -316,22 +316,20 @@ int FileSelector(unsigned char *buffer) y = m_input.ir.y; /* draw wiimote pointer */ - gxResetAngle(m_input.ir.angle); - gxDrawTexture(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,255); - gxResetAngle(0.0); + gxDrawTextureRotate(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,m_input.ir.angle,255); /* find selected item */ - yoffset = PAGEOFFSET - 2; + yoffset = PAGEOFFSET - 4; m->selected = m->max_buttons + 2; for (i = offset; i < (offset + PAGESIZE) && (i < maxfiles); i++) { - if ((x<=380)&&(y>=yoffset)&&(y<(yoffset+24))) + if ((x<=380)&&(y>=yoffset)&&(y<(yoffset+(bar_over.h)))) { selection = i; m->selected = -1; break; } - yoffset += 24; + yoffset += (bar_over.h); } /* find selected button */ diff --git a/source/gx/gui/gui.c b/source/gx/gui/gui.c index 34d3bb5..470943d 100644 --- a/source/gx/gui/gui.c +++ b/source/gx/gui/gui.c @@ -484,9 +484,7 @@ int GUI_UpdateMenu(gui_menu *menu) int y = m_input.ir.y; /* draw wiimote pointer */ - gxResetAngle(m_input.ir.angle); - gxDrawTexture(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,255); - gxResetAngle(0.0); + gxDrawTextureRotate(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,m_input.ir.angle,255); /* check for valid buttons */ selected = max_buttons + 2; @@ -804,9 +802,7 @@ int GUI_WindowPrompt(gui_menu *parent, char *title, char *items[], u8 nb_items) y = m_input.ir.y; /* draw wiimote pointer */ - gxResetAngle(m_input.ir.angle); - gxDrawTexture(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,255); - gxResetAngle(0.0); + gxDrawTextureRotate(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,m_input.ir.angle,255); /* check for valid buttons */ selected = -1; @@ -935,9 +931,7 @@ void GUI_SlideMenuTitle(gui_menu *m, int title_offset) y = m_input.ir.y; /* draw wiimote pointer */ - gxResetAngle(m_input.ir.angle); - gxDrawTexture(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,255); - gxResetAngle(0.0); + gxDrawTextureRotate(w_pointer, x-w_pointer->width/2, y-w_pointer->height/2, w_pointer->width, w_pointer->height,m_input.ir.angle,255); /* check for valid buttons */ m->selected = m->max_buttons + 2; @@ -1010,6 +1004,12 @@ static void *MsgBox_Thread(void *arg) if (message_box.msg) FONT_writeCenter(message_box.msg,18,xwindow,xwindow+message_box.window->width,ypos,(GXColor)WHITE); + /* draw throbber */ + if (message_box.throbber) + { + gxDrawTextureRotate(message_box.throbber,xwindow+(message_box.window->width-message_box.throbber->width)/2,ywindow+message_box.window->height-message_box.throbber->height-20,message_box.throbber->width,message_box.throbber->height,(message_box.progress * 360.0) / 100.0, 255); + } + /* draw exit message */ if (message_box.buttonB) { @@ -1026,6 +1026,10 @@ static void *MsgBox_Thread(void *arg) /* update display */ gxSetScreen(); + + /* update progression */ + message_box.progress++; + if (message_box.progress > 100) message_box.progress = 0; } else { @@ -1045,7 +1049,7 @@ void GUI_MsgBoxUpdate(gui_menu *parent, char *title, char *msg) } /* setup current Message Box */ -void GUI_MsgBoxOpen(char *title, char *msg) +void GUI_MsgBoxOpen(char *title, char *msg, bool throbber) { if (SILENT) return; @@ -1058,6 +1062,7 @@ void GUI_MsgBoxOpen(char *title, char *msg) /* initialize default textures */ message_box.window = gxTextureOpenPNG(Frame_s4_png,0); message_box.top = gxTextureOpenPNG(Frame_s4_title_png,0); + if (throbber) message_box.throbber = gxTextureOpenPNG(Frame_throbber_png,0); /* window position */ int xwindow = (vmode->fbWidth - message_box.window->width)/2; @@ -1093,6 +1098,7 @@ void GUI_MsgBoxOpen(char *title, char *msg) } /* resume LWP thread for MessageBox refresh */ + message_box.progress = 0; message_box.refresh = TRUE; LWP_ResumeThread(msgboxthread); } @@ -1151,6 +1157,7 @@ void GUI_MsgBoxClose(void) gxTextureClose(&message_box.top); gxTextureClose(&message_box.buttonA); gxTextureClose(&message_box.buttonB); + gxTextureClose(&message_box.throbber); } } @@ -1159,7 +1166,8 @@ void GUI_WaitPrompt(char *title, char *msg) if (SILENT) return; /* update message box */ - GUI_MsgBoxOpen(title, msg); + gxTextureClose(&message_box.throbber); + GUI_MsgBoxOpen(title, msg, 0); /* allocate texture memory */ message_box.buttonA = gxTextureOpenPNG(Key_A_png,0); @@ -1198,7 +1206,7 @@ void GUI_Initialize(void) { /* create LWP thread for MessageBox refresh */ message_box.refresh = FALSE; - LWP_CreateThread (&msgboxthread, MsgBox_Thread, NULL, NULL, 0, 50); + LWP_CreateThread (&msgboxthread, MsgBox_Thread, NULL, NULL, 0, 10); LWP_SuspendThread(msgboxthread); } diff --git a/source/gx/gui/gui.h b/source/gx/gui/gui.h index 3f6498d..816ccb4 100644 --- a/source/gx/gui/gui.h +++ b/source/gx/gui/gui.h @@ -121,14 +121,16 @@ typedef struct typedef struct { - bool refresh; /* messagebox current state */ - gui_menu *parent; /* parent menu */ - char title[64]; /* box title */ - char msg[64]; /* box message */ - gx_texture *window; /* pointer to box texture */ - gx_texture *top; /* pointer to box title texture */ - gx_texture *buttonA; /* pointer to button A texture */ - gx_texture *buttonB; /* pointer to button B texture */ + u32 progress; /* progress counter */ + bool refresh; /* messagebox current state */ + gui_menu *parent; /* parent menu */ + char title[64]; /* box title */ + char msg[64]; /* box message */ + gx_texture *window; /* pointer to box texture */ + gx_texture *top; /* pointer to box title texture */ + gx_texture *buttonA; /* pointer to button A texture */ + gx_texture *buttonB; /* pointer to button B texture */ + gx_texture *throbber; /* pointer to throbber texture */ } gui_message; /* Menu Inputs */ @@ -165,6 +167,7 @@ extern const u8 Frame_s3_png[]; extern const u8 Frame_s4_png[]; extern const u8 Frame_s1_title_png[]; extern const u8 Frame_s4_title_png[]; +extern const u8 Frame_throbber_png[]; /* ROM Browser */ extern const u8 Overlay_bar_png[]; @@ -276,7 +279,7 @@ extern int GUI_UpdateMenu(gui_menu *menu); extern int GUI_RunMenu(gui_menu *menu); extern int GUI_WindowPrompt(gui_menu *parent, char *title, char *items[], u8 nb_items); extern void GUI_SlideMenuTitle(gui_menu *m, int title_offset); -extern void GUI_MsgBoxOpen(char *title, char *msg); +extern void GUI_MsgBoxOpen(char *title, char *msg, bool throbber); extern void GUI_MsgBoxUpdate(gui_menu *parent, char *title, char *msg); extern void GUI_MsgBoxClose(void); extern void GUI_WaitPrompt(char *title, char *msg); diff --git a/source/gx/gui/menu.c b/source/gx/gui/menu.c index b67f462..1601928 100644 --- a/source/gx/gui/menu.c +++ b/source/gx/gui/menu.c @@ -285,9 +285,9 @@ static gui_butn buttons_main[9] = {&button_icon_data,BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_FADE|BUTTON_OVER_SFX ,{3,0,1,1}, 80,194,148,132}, {&button_icon_data,BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_FADE|BUTTON_OVER_SFX ,{3,0,1,1},246,194,148,132}, {&button_icon_data,BUTTON_VISIBLE|BUTTON_ACTIVE|BUTTON_FADE|BUTTON_OVER_SFX ,{3,0,1,0},412,194,148,132}, - {NULL , BUTTON_FADE|BUTTON_OVER_SFX ,{3,0,1,1}, 0,360, 88, 48}, - {NULL , BUTTON_FADE|BUTTON_OVER_SFX|BUTTON_SELECT_SFX,{2,1,1,1},542,330, 88, 38}, - {NULL , BUTTON_FADE|BUTTON_OVER_SFX|BUTTON_SELECT_SFX,{1,0,1,0},542,370, 88, 48} + {NULL , BUTTON_FADE|BUTTON_OVER_SFX ,{3,0,1,1}, 10,372, 84, 32}, + {NULL , BUTTON_FADE|BUTTON_OVER_SFX|BUTTON_SELECT_SFX,{2,1,1,1},546,334, 84, 32}, + {NULL , BUTTON_FADE|BUTTON_OVER_SFX|BUTTON_SELECT_SFX,{1,0,1,0},546,372, 84, 32} }; /* Controllers Menu */ @@ -1506,7 +1506,7 @@ static void ctrlmenu(void) case 12: /* Controller Keys Configuration */ if (config.input[player].device < 0) break; - GUI_MsgBoxOpen("Keys Configuration", ""); + GUI_MsgBoxOpen("Keys Configuration", "",0); if (config.input[player].padtype == DEVICE_6BUTTON) { /* 6-buttons gamepad */ diff --git a/source/gx/gx_video.c b/source/gx/gx_video.c index 8eaf6cd..64bee2f 100644 --- a/source/gx/gx_video.c +++ b/source/gx/gx_video.c @@ -662,6 +662,56 @@ void gxDrawTexture(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, u8 alpha) } } +void gxDrawTextureRotate(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, f32 angle, u8 alpha) +{ + if (!texture) return; + if (texture->data) + { + /* load texture object */ + GXTexObj texObj; + GX_InitTexObj(&texObj, texture->data, texture->width, texture->height, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_InitTexObjLOD(&texObj,GX_LINEAR,GX_LIN_MIP_LIN,0.0,10.0,0.0,GX_FALSE,GX_TRUE,GX_ANISO_4); + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_InvalidateTexAll(); + + /* vertex coordinate */ + x -= (vmode->fbWidth/2); + y -= (vmode->efbHeight/2); + + /* Modelview rotation */ + Mtx m,mv; + Vector axis = (Vector) {0,0,1}; + guLookAt(mv, &cam.pos, &cam.up, &cam.view); + guMtxRotAxisDeg (m, &axis, angle); + guMtxTransApply(m,m, x+w/2,y+h/2,0); + guMtxConcat(mv,m,mv); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Flush(); + + /* draw textured quad */ + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position2s16(-w/2,-h/2); + GX_Color4u8(0xff,0xff,0xff,alpha); + GX_TexCoord2f32(0.0, 0.0); + GX_Position2s16(w/2,-h/2); + GX_Color4u8(0xff,0xff,0xff,alpha); + GX_TexCoord2f32(1.0, 0.0); + GX_Position2s16(w/2,h/2); + GX_Color4u8(0xff,0xff,0xff,alpha); + GX_TexCoord2f32(1.0, 1.0); + GX_Position2s16(-w/2,h/2); + GX_Color4u8(0xff,0xff,0xff,alpha); + GX_TexCoord2f32(0.0, 1.0); + GX_End (); + GX_DrawDone(); + + /* restore default Modelview */ + guLookAt(mv, &cam.pos, &cam.up, &cam.view); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Flush(); + } +} + void gxDrawTextureRepeat(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, u8 alpha) { if (!texture) return; @@ -794,27 +844,6 @@ void gxCopyScreenshot(gx_texture *texture) DCFlushRange(texture->data, texture->width * texture->height * 4); } -void gxResetAngle(f32 angle) -{ - Mtx view; - - if (angle) - { - Mtx m,m1; - Vector axis = (Vector) {0,0,1}; - guLookAt(m, &cam.pos, &cam.up, &cam.view); - guMtxRotAxisDeg (m1, &axis, angle); - guMtxConcat(m,m1,view); - } - else - { - guLookAt(view, &cam.pos, &cam.up, &cam.view); - } - - GX_LoadPosMtxImm(view, GX_PNMTX0); - GX_Flush(); -} - void gxSetScreen(void) { GX_CopyDisp(xfb[whichfb], GX_FALSE); diff --git a/source/gx/gx_video.h b/source/gx/gx_video.h index 750e50e..e178167 100644 --- a/source/gx/gx_video.h +++ b/source/gx/gx_video.h @@ -51,9 +51,9 @@ extern u8 gc_pal; extern void gxDrawRectangle(s32 x, s32 y, s32 w, s32 h, u8 alpha, GXColor color); extern void gxDrawTexture(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, u8 alpha); extern void gxDrawTextureRepeat(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, u8 alpha); +extern void gxDrawTextureRotate(gx_texture *texture, s32 x, s32 y, s32 w, s32 h, f32 angle, u8 alpha); extern void gxDrawScreenshot(u8 alpha); extern void gxCopyScreenshot(gx_texture *texture); -extern void gxResetAngle(f32 angle); extern void gxClearScreen(GXColor color); extern void gxSetScreen(void); diff --git a/source/gx/images/Frame_s4_title.png b/source/gx/images/Frame_s4_title.png new file mode 100644 index 0000000000000000000000000000000000000000..05c8b237ae8cd31672d42878f53786457a0a2c74 GIT binary patch literal 2285 zcmbVOi$Bxv7yoRASs1YrVST$`(&o#EG3Js>2vKf@W+I_Bm*sA`q>I~EjcTsBOD;oM zWF(4E6s>Cp9 zO(p@h-b8Y6Aj!wy8+pQ?ObQPs2T+iw|yhecb}Rzvsr&s%2-sJ z#vf*`^)q_9H!fUIn5fAp)mzGrvORp4NLJwA-Tb-W&sNwa&i~LClre>x)g897@=?;k zYve{A-K{*@kf{?ZdG=}DzO3EgSvK|gUB~rzPjauVdzgM#wyfIP|GAm(+B3Ly2+4s^Q-hT$w0D}1l~t)S_6}eTm6Xq{i)5l4px*a{mkOk7;)3CQd9fmEMw2`XyqG=E7#l)am#nI0$9_Xjh zH{?(Hsf>i2eLOEQ|NXiruS#Bqvy7c4_O91l{s?AM6eB0w7~h?z-`D21-o}xq*u1-s z8Xp=F0zLFt!xV=UNLpmUbTsAxeEV1C{V5Ke9-VTeE$3EEm`!6|Cg;I;T!Y^oKP%9U!-l`VYd*D} zJVb>XZU;OL`r2}$%Od3~R`)LYR%rD8Yde0TvkNUy;z2dJlO!ZD$2cK$NzSc$r(U9G zjTX%k7MeJ^z?6Dk)lyb#hOgu9yTa#e9S@-YH~u=H%SdD2`FF--un#K?Pn(h7lUKmV z9~Vphm{G4T30K~nI*jc(yJ!$Wxv-&D&PX1&y7(`eEmTvbKBM6!ZtRSJ$`han1bhBg za3b$XOuB}f3uUPVE~)HsZR}U*ni11?WirYVjbA zIoP*w-cnrRy1$z-6lARmc+<@IMEtc43}?=MF#(^zWyIJo8g$+`)k*YI)MHH;v+JLQ z4fX9g{n4W_*;V<77P~jQO-|_Cpm&(ho}rJ&5=D8Q{^pga{_V)Ic#V|FZsn%HYe8~H z;!DS|a~!RM``;_fnD?BS4+v7nM@)#%a!QnsoQ_2z3EJ~SLMRS zwEfYK4NXsC$3r=YkV2(vv*jVW;A^k~u>%wab`?Ht{9u#%dr75+K~~ zJwwOM*kLe|%I&}Hj8v)PhfhSIluWtZpAe$3+_w!yVU5{!({+Zu>t3nZP>^NuV4EXR z3Z)i*SN&cG2RqiYva-6}!eVXIu3yyYJOD5IF}eCmwdbj8O)Uq9aoM>}JdCC|iA%P-WZ~X#g2|6Bga$*TA%tqNibAlxL| zL0*;HRc?DCcDgRIm3-x&;cr=U&mOY^_V#Lw4Q+bc;K0S_Q>5<4SJo-F~hnF97KKtQ8z zYi;B4N8Al~1ECwbz9ojEA-h8pLBJrmrpSBm7nhjp+x2%|cs%IQhXMBZi_<)})KFCK zg+Hx|G~^)IT1;F;q+oVF`pxFG51+|y%X8BV1cFrWs~tN+(6(j#l>1BKZipoY5*tB* zyJB`qCa`IS=f5@4`cf0PNts1HUYL~da45|NEq8r+25o#nl~7UiPTz>4Pf1$x2zhEx*6~#0%f4$1g{5T?v?2GkHMneCU$j@qLiH;?^UkW<5)F82s zF!jA(1iGzr3)vl#pefRIikWFSdvvN)LFd?FU9sSQ=zjDz0HGkbtFwxp0XTX~DBZev zVWqBUslZ`kV@gx-6nVt5!nU??WQv(JZ}?zr;J9cJ!pC7HbWw z9{Ss4L@E^mv@PQT{a?LY;|a>$zI`VZ1W;k}ou@8JL-?`dzm)B$G06BGAs8W@1`>w< q-!^_??-~;7s5-8wL_-R9>;UY_9IlA+TNS~$0a%&anN^#3(f$X4r#oZ- literal 0 HcmV?d00001 diff --git a/source/gx/images/Frame_throbber.png b/source/gx/images/Frame_throbber.png new file mode 100644 index 0000000000000000000000000000000000000000..98d5316fb84ee9c7fc5714a5b0e5775cb5381a86 GIT binary patch literal 2334 zcmV+(3E}pMP)KaYcY?dV{!8ym!P(IWD z#ntC$H{88Dl}eRVS1xH3fMhbcoHM>F2t?v?-xp{**9AiDMD66tUGMEX*jQ>^rPP6h zkT)3TQZA>#Fid0yv-qrk0QJdQY1-7gb^_R4O5KtbVvNNIvEg|hY{!K~2u#DoC&yYa zX+ktsYTfG;N~hDq-xG;MBj-Hf`#v;H+g^NLTpK|Uz-K;O*9D~%mT7`g3#Mhjw5;pu zIlgFQzSkTI$uZk;-E3BEwQc(2Z{I(w_r|q0N}+~^hUryRRhsL%@3^kp?E8Lmd3kvt zgm`{r9_O4%0wC0eq3h5z6_#ltQ4_<^P?i<*EZg5$H#z=}W!Z_t$4^JjwqJSBTN16r)2hsGi=XPA*)&QR8BQuyqUM;{d3=m?$b6u!;70geT-x5d&(cyU> zJkJBA6oz3SpU)$o&qLRBj2ky@ICtb?Or=uSfsBZj@~{FC1jyy{C=@h!t_{u@5|b-& zbtqf7almzJC>##&^*m45wml4!%jJ;GW?@+t^7;IpubKF!Q8_VkyyJY==_OBo7vm#g zxDEmH9L%{liHYMYaOzZg^(__3WHMJ6V}CMDbET@P!|G59_kowhz-##ovuz>jtuWb6w*R>c@shjKnDwLv|*4Eap_B`+DAP5iy z!6nl)hZQMx=e{wDbE!Jn7>`Z(c{p6AKnV0+$rLX2^#1hGh4XhS08mv`)x|lVA%wt} zhWL0W6nb`GU|@D#U0v^((0m!X_IS0mk%%3|e)-YrGuDAZWTT&`3 zD?gHDc}CHh8Drp_!}GjIG#dRBz?88BO4L@rc;v(xoIKruY)(Z%H_&?aJbw7h_oeAm zr)-ls=Z`YR;QKy`pN`{z5CY4x5{C{QdSt9%)BbWa z1q}1=pNWq17cw&E{Ah9CB7`89%b~x&ADK)Bwrv;ItXXq>tbhRE(^IXR9$PpM56)|V zA`3*qG8QhFgGfc0NT*M)R$SM8f-!c{G|icsra@IzXqpCDmhFA}_RU?qc=1^B;IW4n ztpD4tzvJNt=lx>QLvs~E2)Zx#7EYWvx$2GAckGc`TCR1Zv$M0wb={|Px!fdGRnv`) zjl;!#LgZvw{)uUtM}cpySh1EA3NKQ%!dtQ| zRr{WgEB%>wADG*)@@BPedh3YC<5ZHQ6|yYHBuR>K&Sw~g@rkbMqmB!=3)>4;M=HXH zUDum&=4@xIz4KBmnW$OO^Y1=-v;0v&@p$~bP$(2BI(N}1P1BS-&)a&ZK(UGAwzjrk zkoO+^2p=D7!QKNO;@4Yt!lriQ-F^Rj@5cO5E#sVj4**6UWhtegl)|=cVfP*!9Q;bns;W(~SS*NCgqtUfk2VW|pyPbk#;Ydt66B84q{Qbjtj;Z zvbj7&z$HG4RDz zNC>3q$=a9p?f*bZrBW{eXelo*1Lq7{K?k7(zVCwjF7B#}Vq(B9GU%54=QgzWG<7v3SHMFP1B(3Isjz$@8AE>k|j$306`Ea zgi=u3hGh~Mx&g~F(cRsHyXzCfb+g&*cL1Dx;kjoMU0vN!)jS9xqOIfeH#fe#VboFf zrf)yO;qa=r-+ue$L?ZDFrS!hu-rhrN*RI{OY}vBm9L_m7pC~W!q~zmxW|`abNG{_Rl-d?LDyn{VO+{yJV+sZ*SKKwPP19cB8kiAAMH_FgSP> zwKbCvn>4ZT`>n55mQuIm*D<#3ta2S&l$V7d0)~(*F!in~L@Ua~XD3hH@pb9H1`37b zJ$-#wmIndP+*6;#{d4X`G!n_QwVz+UecR~!);Ep+0KefyUOGUa;s5{u07*qoM6N<$ Ef=@zlhX4Qo literal 0 HcmV?d00001