#include "common.h" #ifdef DEBUGMENU #include "RwHelper.h" #include "Pad.h" #include "ControllerConfig.h" #include "Timer.h" #include "rtcharse.h" #include "inttypes.h" #include "debugmenu.h" #include #ifdef _WIN32 #define snprintf _snprintf #define strdup _strdup #endif // Font stuff struct Pt { int x, y; }; enum MenuFontStyle { MENUFONT_NORMAL, MENUFONT_SEL_ACTIVE, MENUFONT_SEL_INACTIVE, MENUFONT_MOUSE }; RtCharset *fontStyles[4]; RtCharsetDesc fontDesc; int fontscale = 1; // not supported right now Pt fontGetStringSize(const char *s) { Pt sz = { 0, 0 }; int x; char c; sz.y = fontDesc.height*fontscale; // always assume one line; x = 0; while(c = *s++){ if(c == '\n'){ sz.y += fontDesc.height*fontscale; if(x > sz.x) sz.x = x; x = 0; }else x += fontDesc.width*fontscale; } if(x > sz.x) sz.x = x; return sz; } Pt fontPrint(const char *s, float x, float y, int style) { RtCharsetPrintBuffered(fontStyles[style], s, x, y, false); return fontGetStringSize(s); } int fontGetLen(int len) { return len*fontDesc.width*fontscale; } void createMenuFont(void) { OpenCharsetSafe(); RwRGBA fg_normal = { 255, 255, 255, 255 }; RwRGBA bg_normal = { 255, 255, 255, 0 }; fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal); assert(fontStyles[MENUFONT_NORMAL]); RwRGBA fg_sel_active = { 200, 200, 200, 255 }; RwRGBA bg_sel_active = { 132, 132, 132, 255 }; fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active); assert(fontStyles[MENUFONT_SEL_ACTIVE]); RwRGBA fg_sel_inactive = { 200, 200, 200, 255 }; RwRGBA bg_sel_inactive = { 200, 200, 200, 0 }; fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive); assert(fontStyles[MENUFONT_SEL_INACTIVE]); RwRGBA fg_mouse = { 255, 255, 255, 255 }; RwRGBA bg_mouse = { 132, 132, 132, 255 }; fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse); assert(fontStyles[MENUFONT_MOUSE]); RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc); } void destroyMenuFont(void) { RtCharsetDestroy(fontStyles[MENUFONT_NORMAL]); fontStyles[MENUFONT_NORMAL] = nil; RtCharsetDestroy(fontStyles[MENUFONT_SEL_ACTIVE]); fontStyles[MENUFONT_SEL_ACTIVE] = nil; RtCharsetDestroy(fontStyles[MENUFONT_SEL_INACTIVE]); fontStyles[MENUFONT_SEL_INACTIVE] = nil; RtCharsetDestroy(fontStyles[MENUFONT_MOUSE]); fontStyles[MENUFONT_MOUSE] = nil; } enum EntryType { MENUEMPTY = 0, MENUSUB, MENUVAR, MENUVAR_INT, MENUVAR_FLOAT, MENUVAR_CMD, MENUSCROLL // dummy }; struct Menu { Menu *parent; RwRect r; MenuEntry *entries; int numEntries; int maxNameWidth, maxValWidth; MenuEntry *findEntry(const char *entryname); void insertEntrySorted(MenuEntry *entry); void appendEntry(MenuEntry *entry); bool isScrollingUp, isScrollingDown; int scrollStart; int numVisible; RwRect scrollUpR, scrollDownR; void scroll(int off); int selection; MenuEntry *selectedEntry; // updated by update void changeSelection(int newsel); void changeSelection(MenuEntry *e); void update(void); void draw(void); Menu(void){ memset(this, 0, sizeof(Menu)); } ~Menu(void); }; extern Menu toplevel; struct MenuEntry_Sub : MenuEntry { Menu *submenu; MenuEntry_Sub(const char *name, Menu *menu); ~MenuEntry_Sub(void) { delete submenu; } }; struct MenuEntry_Var : MenuEntry { int maxvallen; int vartype; bool wrapAround; virtual void processInput(bool mouseOver, bool selected) = 0; int getValWidth(void) { return maxvallen; } virtual void getValStr(char *str, int len) = 0; MenuEntry_Var(const char *name, int type); }; struct MenuEntry_Int : MenuEntry_Var { virtual void setStrings(const char **strings) = 0; virtual int findStringLen(void) = 0; MenuEntry_Int(const char *name); }; #define INTTYPES \ X(Int8, int8, 4, "%4" PRId8) \ X(Int16, int16, 6, "%6" PRId16) \ X(Int32, int32, 11, "%11" PRId32) \ X(Int64, int64, 21, "%21" PRId64) \ X(UInt8, uint8, 4, "%4" PRIu8) \ X(UInt16, uint16, 6, "%6" PRIu16) \ X(UInt32, uint32, 11, "%11" PRIu32) \ X(UInt64, uint64, 21, "%21" PRIu64) #define FLOATTYPES \ X(Float32, float, 11, "%11.3f") \ X(Float64, double, 11, "%11.3lf") #define X(NAME, TYPE, MAXLEN, FMT) \ struct MenuEntry_##NAME : MenuEntry_Int \ { \ TYPE *variable; \ TYPE lowerBound, upperBound; \ TYPE step; \ TriggerFunc triggerFunc; \ const char *fmt; \ const char **strings; \ \ void processInput(bool mouseOver, bool selected); \ void getValStr(char *str, int len); \ \ void setStrings(const char **strings); \ int findStringLen(void); \ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \ }; INTTYPES #undef X #define X(NAME, TYPE, MAXLEN, FMT) \ struct MenuEntry_##NAME : MenuEntry_Var \ { \ TYPE *variable; \ TYPE lowerBound, upperBound; \ TYPE step; \ TriggerFunc triggerFunc; \ const char *fmt; \ \ void processInput(bool mouseOver, bool selected); \ void getValStr(char *str, int len); \ \ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \ }; FLOATTYPES #undef X struct MenuEntry_Cmd : MenuEntry_Var { TriggerFunc triggerFunc; void processInput(bool mouseOver, bool selected); void getValStr(char *str, int len); MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc); }; Menu *findMenu(const char *name); #define MUHKEYS \ X(leftjustdown, rsLEFT) \ X(rightjustdown, rsRIGHT) \ X(upjustdown, rsUP) \ X(downjustdown, rsDOWN) \ X(pgupjustdown, rsPGUP) \ X(pgdnjustdown, rsPGDN) #define MUHBUTTONS \ X(button1justdown, 1) \ X(button2justdown, 2) \ X(button3justdown, 3) #define REPEATDELAY 700 #define REPEATINTERVAL 50 #define X(var, keycode) static int var; MUHKEYS #undef X static int downtime; static int repeattime; static int lastkeydown; static int *keyptr; static int buttondown[3]; static int lastbuttondown; static int *buttonptr; static int button1justdown, button2justdown, button3justdown; static float mouseX, mouseY; static int menuOn; static int menuInitialized; static int screenWidth, screenHeight; static RwRaster *cursor, *arrow; static int firstBorder = 10; static int topBorder = 40; //10; static int leading = 4; static int gap = 10; static int minwidth = 100; void drawMouse(void); void drawArrow(RwRect r, int direction, int style); Menu toplevel; Menu *activeMenu = &toplevel; Menu *deepestMenu = &toplevel; Menu *mouseOverMenu; MenuEntry *mouseOverEntry; MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies #define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) #define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) #define CTRLJUSTDOWN(key) \ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) #define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) bool isMouseInRect(RwRect r) { return (mouseX >= r.x && mouseX < r.x+r.w && mouseY >= r.y && mouseY < r.y+r.h); } /* * MenuEntry */ MenuEntry::MenuEntry(const char *name) { this->type = MENUEMPTY; this->name = strdup(name); this->next = nil; this->menu = nil; } MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu) : MenuEntry(name) { this->type = MENUSUB; this->submenu = menu; } MenuEntry_Var::MenuEntry_Var(const char *name, int vartype) : MenuEntry(name) { this->type = MENUVAR; this->vartype = vartype; this->maxvallen = 0; this->wrapAround = false; } /* * ***************************** * MenuEntry_Int * ***************************** */ MenuEntry_Int::MenuEntry_Int(const char *name) : MenuEntry_Var(name, MENUVAR_INT) { } #define X(NAME, TYPE, MAXLEN, FMT) \ int \ MenuEntry_##NAME::findStringLen(void){ \ TYPE i; \ int len, maxlen = 0; \ for(i = this->lowerBound; i <= this->upperBound; i++){ \ len = strlen(this->strings[i-this->lowerBound]); \ if(len > maxlen) \ maxlen = len; \ } \ return maxlen; \ } \ void \ MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ { \ TYPE v, oldv; \ int overflow = 0; \ int underflow = 0; \ \ v = *this->variable; \ oldv = v; \ \ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ v -= this->step; \ if(v > oldv) \ underflow = 1; \ } \ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ v += this->step; \ if(v < oldv) \ overflow = 1; \ } \ if(this->wrapAround){ \ if(v > this->upperBound || overflow) v = this->lowerBound; \ if(v < this->lowerBound || underflow) v = this->upperBound; \ }else{ \ if(v > this->upperBound || overflow) v = this->upperBound; \ if(v < this->lowerBound || underflow) v = this->lowerBound; \ } \ \ *this->variable = v; \ if(oldv != v && this->triggerFunc) \ this->triggerFunc(); \ } \ void \ MenuEntry_##NAME::getValStr(char *str, int len) \ { \ static char tmp[20]; \ if(this->strings){ \ snprintf(tmp, 20, "%%%ds", this->maxvallen); \ if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \ snprintf(str, len, "ERROR"); \ return; \ } \ snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \ }else \ snprintf(str, len, this->fmt, *this->variable); \ } \ void \ MenuEntry_##NAME::setStrings(const char **strings) \ { \ this->strings = strings; \ if(this->strings) \ this->maxvallen = findStringLen(); \ } \ MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ : MenuEntry_Int(name) \ { \ this->variable = ptr; \ this->step = step; \ this->lowerBound = lowerBound; \ this->upperBound = upperBound; \ this->triggerFunc = triggerFunc; \ this->maxvallen = MAXLEN; \ this->fmt = FMT; \ this->setStrings(strings); \ } INTTYPES #undef X /* * ***************************** * MenuEntry_Float * ***************************** */ #define X(NAME, TYPE, MAXLEN, FMT) \ MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ : MenuEntry_Var(name, MENUVAR_FLOAT) \ { \ this->variable = ptr; \ this->step = step; \ this->lowerBound = lowerBound; \ this->upperBound = upperBound; \ this->triggerFunc = triggerFunc; \ this->maxvallen = MAXLEN; \ this->fmt = FMT; \ } \ void \ MenuEntry_##NAME::getValStr(char *str, int len) \ { \ snprintf(str, len, this->fmt, *this->variable); \ } \ void \ MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ { \ float v, oldv; \ int overflow = 0; \ int underflow = 0; \ \ v = *this->variable; \ oldv = v; \ \ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ v -= this->step; \ if(v > oldv) \ underflow = 1; \ } \ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ v += this->step; \ if(v < oldv) \ overflow = 1; \ } \ if(this->wrapAround){ \ if(v > this->upperBound || overflow) v = this->lowerBound; \ if(v < this->lowerBound || underflow) v = this->upperBound; \ }else{ \ if(v > this->upperBound || overflow) v = this->upperBound; \ if(v < this->lowerBound || underflow) v = this->lowerBound; \ } \ \ *this->variable = v; \ if(oldv != v && this->triggerFunc) \ this->triggerFunc(); \ } FLOATTYPES #undef X /* * ***************************** * MenuEntry_Cmd * ***************************** */ void MenuEntry_Cmd::processInput(bool mouseOver, bool selected) { // Don't execute on button3 if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown))) this->triggerFunc(); } void MenuEntry_Cmd::getValStr(char *str, int len) { strncpy(str, "<", len); } MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc) : MenuEntry_Var(name, MENUVAR_CMD) { this->maxvallen = 1; this->triggerFunc = triggerFunc; } /* * ***************************** * Menu * ***************************** */ void Menu::scroll(int off) { if(isScrollingUp && off < 0) scrollStart += off; if(isScrollingDown && off > 0) scrollStart += off; if(scrollStart < 0) scrollStart = 0; if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible; } void Menu::changeSelection(int newsel){ selection = newsel; if(selection < 0) selection = 0; if(selection >= numEntries) selection = numEntries-1; if(selection < scrollStart) scrollStart = selection; if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1; } void Menu::changeSelection(MenuEntry *sel) { MenuEntry *e; int i = 0; for(e = this->entries; e; e = e->next){ if(e == sel){ this->selection = i; this->selectedEntry = sel; break; } i++; } } MenuEntry* Menu::findEntry(const char *entryname) { MenuEntry *m; for(m = this->entries; m; m = m->next) if(strcmp(entryname, m->name) == 0) return m; return nil; } void Menu::insertEntrySorted(MenuEntry *entry) { MenuEntry **mp; int cmp; for(mp = &this->entries; *mp; mp = &(*mp)->next){ cmp = strcmp(entry->name, (*mp)->name); if(cmp == 0) return; if(cmp < 0) break; } entry->next = *mp; *mp = entry; entry->menu = this; this->numEntries++; } void Menu::appendEntry(MenuEntry *entry) { MenuEntry **mp; for(mp = &this->entries; *mp; mp = &(*mp)->next); entry->next = *mp; *mp = entry; entry->menu = this; this->numEntries++; } void Menu::update(void) { int i; int x, y; Pt sz; MenuEntry *e; int onscreen; x = this->r.x; y = this->r.y + 18; int end = this->r.y+this->r.h - 18; this->numVisible = 0; deepestMenu = this; int bottomy = end; onscreen = 1; i = 0; this->maxNameWidth = 0; this->maxValWidth = 0; this->isScrollingUp = this->scrollStart > 0; this->isScrollingDown = false; this->selectedEntry = nil; for(e = this->entries; e; e = e->next){ sz = fontGetStringSize(e->name); e->r.x = x; e->r.y = y; e->r.w = sz.x; e->r.h = sz.y; if(i == this->selection) this->selectedEntry = e; if(i >= this->scrollStart) y += sz.y + leading*fontscale; if(y >= end){ this->isScrollingDown = true; onscreen = 0; }else bottomy = y; if(i >= this->scrollStart && onscreen) this->numVisible++; if(e->type == MENUVAR){ int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); if(valwidth > maxValWidth) maxValWidth = valwidth; } if(e->r.w > maxNameWidth) maxNameWidth = e->r.w; i++; } if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale) this->r.w = maxNameWidth + maxValWidth + gap*fontscale; this->scrollUpR = this->r; this->scrollUpR.h = 16; this->scrollDownR = this->scrollUpR; this->scrollDownR.y = bottomy; // Update active submenu if(this->selectedEntry && this->selectedEntry->type == MENUSUB){ Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu; submenu->r.x = this->r.x+this->r.w + 10; submenu->r.y = this->r.y; submenu->r.w = minwidth; // update menu will expand submenu->r.h = this->r.h; submenu->update(); } } void Menu::draw(void) { static char val[100]; int i; MenuEntry *e; i = 0; for(e = this->entries; e; e = e->next){ if(i >= this->scrollStart+this->numVisible) break; if(i >= this->scrollStart){ int style = MENUFONT_NORMAL; if(i == this->selection) style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE; if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry) style = MENUFONT_MOUSE; fontPrint(e->name, e->r.x, e->r.y, style); if(e->type == MENUVAR){ int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); ((MenuEntry_Var*)e)->getValStr(val, 100); fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style); } } i++; } if(this->isScrollingUp) drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR)); if(this->isScrollingDown) drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR)); if(this->selectedEntry && this->selectedEntry->type == MENUSUB) ((MenuEntry_Sub*)this->selectedEntry)->submenu->draw(); } Menu::~Menu(void) { MenuEntry *e, *next; for(e = entries; e; e = next){ next = e->next; delete e; } memset(this, 0, sizeof(Menu)); } Menu* findMenu(const char *name) { Menu *m; MenuEntry *e; char *tmppath = strdup(name); char *next, *curname; curname = tmppath; next = curname; m = &toplevel; while(*next){ curname = next; while(*next){ if(*next == '|'){ *next++ = '\0'; break; } next++; } e = m->findEntry(curname); if(e){ // return an error if the entry exists but isn't a menu if(e->type != MENUSUB){ free(tmppath); return nil; } m = ((MenuEntry_Sub*)e)->submenu; }else{ // Create submenus that don't exist yet Menu *submenu = new Menu(); submenu->parent = m; MenuEntry *me = new MenuEntry_Sub(curname, submenu); // Don't sort submenus outside the toplevel menu if(m == &toplevel) m->insertEntrySorted(me); else m->appendEntry(me); m = submenu; } } free(tmppath); return m; } /* * **************** * debug menu * **************** */ static uint8 cursorPx[] = { #include "cursor.inc" }; static uint8 arrowPx[] = { #include "arrow.inc" }; void DebugMenuInit(void) { createMenuFont(); RwInt32 w, h, d, flags; RwImage *img = RwImageCreate(16, 16, 32); assert(img); RwImageSetPixels(img, cursorPx); RwImageSetStride(img, RwImageGetWidth(img)*4); RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); cursor = RwRasterCreate(w, h, d, flags); cursor = RwRasterSetFromImage(cursor, img); assert(cursor); RwImageDestroy(img); img = RwImageCreate(32, 16, 32); assert(img); RwImageSetPixels(img, arrowPx); RwImageSetStride(img, RwImageGetWidth(img)*4); RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); arrow = RwRasterCreate(w, h, d, flags); arrow = RwRasterSetFromImage(arrow, img); assert(arrow); RwImageDestroy(img); menuInitialized = true; } void DebugMenuShutdown(void) { if(menuInitialized){ destroyMenuFont(); RwRasterDestroy(cursor); cursor = nil; RwRasterDestroy(arrow); arrow = nil; toplevel.~Menu(); new (&toplevel) Menu(); activeMenu = &toplevel; deepestMenu = &toplevel; mouseOverMenu = nil; mouseOverEntry = nil; } menuInitialized = false; } void processInput(void) { int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT); #define X(var, keycode) var = KEYJUSTDOWN(keycode); MUHKEYS #undef X // Implement auto-repeat #define X(var, keycode) \ if(var){ \ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ lastkeydown = keycode; \ keyptr = &var; \ } MUHKEYS #undef X if(lastkeydown){ if(KEYDOWN(lastkeydown)){ int curtime = CTimer::GetTimeInMilliseconds(); if(curtime - downtime > REPEATDELAY){ if(curtime - repeattime > REPEATINTERVAL){ repeattime = curtime; *keyptr = 1; } } }else{ lastkeydown = 0; } } // Also for mouse buttons #define X(var, num) \ if(var){ \ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ lastbuttondown = num; \ buttonptr = &var; \ } MUHBUTTONS #undef X if(lastbuttondown){ if(buttondown[lastbuttondown-1]){ int curtime = CTimer::GetTimeInMilliseconds(); if(curtime - downtime > REPEATDELAY){ if(curtime - repeattime > REPEATINTERVAL){ repeattime = curtime; *buttonptr = 1; } } }else{ lastbuttondown = 0; } } // Walk through all visible menus and figure out which one the mouse is over mouseOverMenu = nil; mouseOverEntry = nil; Menu *menu; for(menu = deepestMenu; menu; menu = menu->parent) if(isMouseInRect(menu->r)){ mouseOverMenu = menu; break; } if(mouseOverMenu){ // Walk all visibile entries and figure out which one the mouse is over MenuEntry *e; int i = 0; for(e = mouseOverMenu->entries; e; e = e->next){ if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible) break; if(i >= mouseOverMenu->scrollStart){ RwRect r = e->r; r.w = mouseOverMenu->r.w; // span the whole menu if(isMouseInRect(r)){ mouseOverEntry = e; break; } } i++; } if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){ mouseOverEntry = &scrollUpEntry; mouseOverEntry->r = mouseOverMenu->scrollUpR; mouseOverEntry->menu = mouseOverMenu; mouseOverEntry->type = MENUSCROLL; } if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){ mouseOverEntry = &scrollDownEntry; mouseOverEntry->r = mouseOverMenu->scrollDownR; mouseOverEntry->menu = mouseOverMenu; mouseOverEntry->type = MENUSCROLL; } } if(pgupjustdown) activeMenu->scroll(shift ? -5 : -1); if(pgdnjustdown) activeMenu->scroll(shift ? 5 : 1); if(downjustdown) activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1)); if(upjustdown) activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1)); if(CPad::NewMouseControllerState.WHEELUP){ if(mouseOverMenu) activeMenu = mouseOverMenu; activeMenu->scroll(shift ? -5 : -1); } if(CPad::NewMouseControllerState.WHEELDN){ if(mouseOverMenu) activeMenu = mouseOverMenu; activeMenu->scroll(shift ? 5 : 1); } if(mouseOverEntry == &scrollUpEntry){ if(button1justdown){ activeMenu = mouseOverEntry->menu; activeMenu->scroll(shift ? -5 : -1); } } if(mouseOverEntry == &scrollDownEntry){ if(button1justdown){ activeMenu = mouseOverEntry->menu; activeMenu->scroll(shift ? 5 : 1); } } // Have to call this before processInput below because menu entry can change if((button1justdown || button3justdown) && mouseOverEntry){ activeMenu = mouseOverEntry->menu; activeMenu->changeSelection(mouseOverEntry); } if(KEYJUSTDOWN(rsENTER)){ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB) activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu; }else if(KEYJUSTDOWN(rsBACKSP)){ if(activeMenu->parent) activeMenu = activeMenu->parent; }else{ if(mouseOverEntry && mouseOverEntry->type == MENUVAR) ((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry); if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR && mouseOverEntry != activeMenu->selectedEntry) ((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true); } } void updateMouse(void) { CPad *pad = CPad::GetPad(0); int dirX = 1; int dirY = 1; if(MousePointerStateHelper.bInvertHorizontally) dirX = -1; if(MousePointerStateHelper.bInvertVertically) dirY = -1; mouseX += pad->NewMouseControllerState.x*dirX; mouseY += pad->NewMouseControllerState.y*dirY; if(mouseX < 0.0f) mouseX = 0.0f; if(mouseY < 0.0f) mouseY = 0.0f; if(mouseX >= screenWidth) mouseX = screenWidth; if(mouseY >= screenHeight) mouseY = screenHeight; button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB; button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB; button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB; buttondown[0] = pad->NewMouseControllerState.LMB; buttondown[1] = pad->NewMouseControllerState.MMB; buttondown[2] = pad->NewMouseControllerState.RMB; // Zero the mouse position so the camera won't move pad->NewMouseControllerState.x = 0.0f; pad->NewMouseControllerState.y = 0.0f; } void DebugMenuProcess(void) { // We only process some input here CPad *pad = CPad::GetPad(0); if(CTRLJUSTDOWN('M')) menuOn = !menuOn; if(!menuOn) return; pad->DisablePlayerControls = 1; // TODO: this could happen earlier if(!menuInitialized) DebugMenuInit(); updateMouse(); } #ifdef LIBRW #define CURRENTCAM (rw::engine->currentCamera) #else #define CURRENTCAM ((RwCamera*)RWSRCGLOBAL(curCamera)) #endif void DebugMenuRender(void) { if(!menuOn) return; RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); RwCamera *cam = CURRENTCAM; screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam)); screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam)); // if(screenHeight > 1080) // fontscale = 2; // else fontscale = 1; Pt sz; sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0); toplevel.r.x = firstBorder*fontscale; toplevel.r.y = topBorder + sz.y + 10; toplevel.r.w = minwidth; // update menu will expand toplevel.r.h = screenHeight - 10 - toplevel.r.y; toplevel.update(); toplevel.draw(); processInput(); RtCharsetBufferFlush(); drawMouse(); } void drawArrow(RwRect r, int direction, int style) { static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; static RwIm2DVertex arrowVerts[4]; RwCamera *cam = CURRENTCAM; float recipz = 1.0f/RwCameraGetNearClipPlane(cam); int width = RwRasterGetWidth(arrow); int height = RwRasterGetHeight(arrow); int left = r.x + (r.w - width)/2; int right = left + width; int top = r.y; int bottom = r.y+r.h; float umin = HALFPX / width; float vmin = HALFPX / height; float umax = (width + HALFPX) / width; float vmax = (height + HALFPX) / height; if(direction < 0){ vmin = (height - HALFPX) / height; vmax = -HALFPX / height; } if(style){ RwIm2DVertexSetScreenX(&arrowVerts[0], r.x); RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1); RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w); RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1); RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[2], r.x); RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1); RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w); RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1); RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); } RwIm2DVertexSetScreenX(&arrowVerts[0], left); RwIm2DVertexSetScreenY(&arrowVerts[0], top); RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[0], umin, recipz); RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz); RwIm2DVertexSetScreenX(&arrowVerts[1], right); RwIm2DVertexSetScreenY(&arrowVerts[1], top); RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[1], umax, recipz); RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz); RwIm2DVertexSetScreenX(&arrowVerts[2], left); RwIm2DVertexSetScreenY(&arrowVerts[2], bottom); RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[2], umin, recipz); RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz); RwIm2DVertexSetScreenX(&arrowVerts[3], right); RwIm2DVertexSetScreenY(&arrowVerts[3], bottom); RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[3], umax, recipz); RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); } void drawMouse(void) { static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; static RwIm2DVertex vertices[4]; RwIm2DVertex *vert; RwCamera *cam; cam = CURRENTCAM; float x = mouseX; float y = mouseY; float w = RwRasterGetWidth(cursor); float h = RwRasterGetHeight(cursor); float recipz = 1.0f/RwCameraGetNearClipPlane(cam); float umin = HALFPX / w; float vmin = HALFPX / h; float umax = (w + HALFPX) / w; float vmax = (h + HALFPX) / h; vert = vertices; RwIm2DVertexSetScreenX(vert, x); RwIm2DVertexSetScreenY(vert, y); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umin, recipz); RwIm2DVertexSetV(vert, vmin, recipz); vert++; RwIm2DVertexSetScreenX(vert, x+w); RwIm2DVertexSetScreenY(vert, y); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umax, recipz); RwIm2DVertexSetV(vert, vmin, recipz); vert++; RwIm2DVertexSetScreenX(vert, x); RwIm2DVertexSetScreenY(vert, y+h); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umin, recipz); RwIm2DVertexSetV(vert, vmax, recipz); vert++; RwIm2DVertexSetScreenX(vert, x+w); RwIm2DVertexSetScreenY(vert, y+h); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umax, recipz); RwIm2DVertexSetV(vert, vmax, recipz); vert++; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6); } /* * Generate interfaces */ #define X(NAME, TYPE, unused1, unused2) \ MenuEntry* \ DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ { \ Menu *m = findMenu(path); \ if(m == nil) \ return nil; \ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \ m->appendEntry(e); \ return e; \ } INTTYPES #undef X #define X(NAME, TYPE, unused1, unused2) \ MenuEntry* \ DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ { \ Menu *m = findMenu(path); \ if(m == nil) \ return nil; \ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \ m->appendEntry(e); \ return e; \ } FLOATTYPES #undef X MenuEntry* \ DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc) { Menu *m = findMenu(path); if(m == nil) return nil; MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc); m->appendEntry(e); return e; } void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap) { if(e && e->type == MENUVAR) ((MenuEntry_Var*)e)->wrapAround = wrap; } void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings) { if(e && e->type == MENUVAR_INT) ((MenuEntry_Int*)e)->setStrings(strings); } void DebugMenuEntrySetAddress(MenuEntry *e, void *addr) { if(e && e->type == MENUVAR){ MenuEntry_Var *ev = (MenuEntry_Var*)e; // HACK - we know the variable field is at the same address // for all int/float classes. let's hope it stays that way. if(ev->vartype = MENUVAR_INT) ((MenuEntry_Int32*)e)->variable = (int32*)addr; else if(ev->vartype = MENUVAR_FLOAT) ((MenuEntry_Float32*)e)->variable = (float*)addr; } } #endif