/* ** settingsmenu.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com> ** ** mkxp is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) any later version. ** ** mkxp is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with mkxp. If not, see <http://www.gnu.org/licenses/>. */ #include "settingsmenu.h" #include <SDL_video.h> #include <SDL_ttf.h> #include <SDL_surface.h> #include <SDL_keyboard.h> #include <SDL_rect.h> #include "keybindings.h" #include "eventthread.h" #include "font.h" #include "input.h" #include "etc-internal.h" #include "util.h" #include <algorithm> #include <assert.h> const Vec2i winSize(540, 356); const uint8_t cBgNorm = 50; const uint8_t cBgDark = 20; const uint8_t cLine = 0; const uint8_t cText = 255; const uint8_t frameWidth = 4; const uint8_t fontSize = 15; static bool pointInRect(const SDL_Rect &r, int x, int y) { return (x >= r.x && x <= r.x+r.w && y >= r.y && y <= r.y+r.h); } typedef SettingsMenuPrivate SMP; #define BTN_STRING(btn) { Input:: btn, #btn } struct VButton { Input::ButtonCode code; const char *str; } static const vButtons[] = { BTN_STRING(Up), BTN_STRING(Down), BTN_STRING(L), BTN_STRING(Left), BTN_STRING(Right), BTN_STRING(R), BTN_STRING(A), BTN_STRING(B), BTN_STRING(C), BTN_STRING(X), BTN_STRING(Y), BTN_STRING(Z) }; static elementsN(vButtons); /* Human readable string representation */ std::string sourceDescString(const SourceDesc &src) { char buf[128]; char pos; switch (src.type) { case Invalid: return std::string(); case Key: { if (src.d.scan == SDL_SCANCODE_LSHIFT) return "Shift"; SDL_Keycode key = SDL_GetKeyFromScancode(src.d.scan); const char *str = SDL_GetKeyName(key); if (*str == '\0') return "Unknown key"; else return str; } case JButton: snprintf(buf, sizeof(buf), "JS %d", src.d.jb); return buf; case JHat: switch(src.d.jh.pos) { case SDL_HAT_UP: pos = 'U'; break; case SDL_HAT_DOWN: pos = 'D'; break; case SDL_HAT_LEFT: pos = 'L'; break; case SDL_HAT_RIGHT: pos = 'R'; break; default: pos = '-'; } snprintf(buf, sizeof(buf), "Hat %d:%c", src.d.jh.hat, pos); return buf; case JAxis: snprintf(buf, sizeof(buf), "Axis %d%c", src.d.ja.axis, src.d.ja.dir == Negative ? '-' : '+'); return buf; } assert(!"unreachable"); return ""; } struct Widget { /* Widgets have a static size and position, * defined at creation */ Widget(SMP *p, const IntRect &rect); /* Public methods take coordinates in global * window coordinates */ bool hit(int x, int y); void draw(SDL_Surface *surf); void motion(int x, int y); void leave(); void click(int x, int y, uint8_t button); protected: SMP *p; IntRect rect; /* Protected abstract methods are called with * widget-local coordinates */ virtual void drawHandler(SDL_Surface *surf) = 0; virtual void motionHandler(int x, int y) = 0; virtual void leaveHandler() = 0; virtual void clickHandler(int x, int y, uint8_t button) = 0; }; struct BindingWidget : Widget { VButton vb; /* Source slots */ SourceDesc src[4]; /* Flag indicating whether a slot source is used * for multiple button targets (red indicator) */ bool dupFlag[4]; BindingWidget(int vbIndex, SMP *p, const IntRect &rect) : Widget(p, rect), vb(vButtons[vbIndex]), hoveredCell(-1) {} void appendBindings(BDescVec &d) const; protected: int hoveredCell; void setHoveredCell(int cell); /* Get the slot cell index that contains (x,y), * or -1 if none */ int cellIndex(int x, int y) const; void drawHandler(SDL_Surface *surf); void motionHandler(int x, int y); void leaveHandler(); void clickHandler(int x, int y, uint8_t button); }; struct Button : Widget { typedef void (SMP::*Callback)(); const char *str; Callback cb; Button(SMP *p, const IntRect &rect, const char *str, Callback cb) : Widget(p, rect), str(str), cb(cb), hovered(false) {} protected: bool hovered; void setHovered(bool val); void drawHandler(SDL_Surface *surf); void motionHandler(int, int); void leaveHandler(); void clickHandler(int, int, uint8_t button); }; struct Label : Widget { const char *str; SDL_Color c; Label() : Widget(0, IntRect()) {} Label(SMP *p, const IntRect &rect, const char *str, uint8_t r, uint8_t g, uint8_t b) : Widget(p, rect), str(str), visible(true) { c.r = r; c.g = g; c.b = b; c.a = 255; } void setVisible(bool val); protected: bool visible; void drawHandler(SDL_Surface *surf); void motionHandler(int, int) {} void leaveHandler() {} void clickHandler(int, int, uint8_t) {} }; enum State { Idle, AwaitingInput }; enum Justification { Left, Center }; struct SettingsMenuPrivate { State state; /* Necessary to decide which window gets to * process joystick events */ bool hasFocus; /* Tell the outer EventThread to destroy us */ bool destroyReq; /* Offset added for all draw calls */ Vec2i drawOff; SDL_Window *window; SDL_Surface *winSurf; uint32_t winID; TTF_Font *font; SDL_PixelFormat *rgb; RGSSThreadData &rtData; std::vector<BindingWidget> bWidgets; std::vector<Button> buttons; Label infoLabel; Label dupWarnLabel; std::vector<Widget*> widgets; Widget *hovered; SourceDesc *captureDesc; const char *captureName; SettingsMenuPrivate(RGSSThreadData &rtData) : rtData(rtData) {} SDL_Surface *createSurface(int w, int h) { int bpp; Uint32 rMask, gMask, bMask, aMask; SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rMask, &gMask, &bMask, &aMask); return SDL_CreateRGBSurface(0, w, h, bpp, rMask, gMask, bMask, 0); } void fillSurface(SDL_Surface *surf, uint8_t grey) { SDL_FillRect(surf, 0, SDL_MapRGBA(rgb, grey, grey, grey, 255)); } void fillRect(SDL_Surface *surf, int x, int y, int w, int h, uint8_t r, uint8_t g, uint8_t b) { SDL_Rect rect = { drawOff.x+x, drawOff.y+y, w, h }; SDL_FillRect(surf, &rect, SDL_MapRGB(rgb, r, g, b)); } void fillRect(SDL_Surface *surf, uint8_t grey, int x, int y, int w, int h) { fillRect(surf, x, y, w, h, grey, grey, grey); } void strokeLineH(SDL_Surface *surf, uint8_t grey, int x, int y, int length, int width) { fillRect(surf, grey, x, y-width/2, length, width); } void strokeLineV(SDL_Surface *surf, uint8_t grey, int x, int y, int length, int width) { fillRect(surf, grey, x-width/2, y, width, length); } void strokeLineH(SDL_Surface *surf, uint8_t r, uint8_t g, uint8_t b, int x, int y, int length, int width) { fillRect(surf, r, g, b, x, y-width/2, length, width); } void strokeLineV(SDL_Surface *surf, uint8_t r, uint8_t g, uint8_t b, int x, int y, int length, int width) { fillRect(surf, r, g, b, x-width/2, y, width, length); } void strokeRect(SDL_Surface *surf, uint8_t grey, int x, int y, int w, int h, int lineW) { strokeLineH(surf, grey, x, y, w, lineW); strokeLineH(surf, grey, x, y+h, w, lineW); strokeLineV(surf, grey, x, y, h, lineW); strokeLineV(surf, grey, x+w, y, h, lineW); } void strokeRectInner(SDL_Surface *surf, int x, int y, int w, int h, int lineW, uint8_t r, uint8_t g, uint8_t b) { fillRect(surf, x, y, w, lineW, r, g, b); fillRect(surf, x, y+h-lineW, w, lineW, r, g, b); fillRect(surf, x, y, lineW, h, r, g, b); fillRect(surf, x+w-lineW, y, lineW, h, r, g ,b); } void strokeRectInner(SDL_Surface *surf, uint8_t grey, int x, int y, int w, int h, int lineW) { strokeRectInner(surf, x, y, w, h, lineW, grey, grey, grey); } void applyFontStyle(bool bold) { if (bold) TTF_SetFontStyle(font, TTF_STYLE_BOLD); else TTF_SetFontStyle(font, TTF_STYLE_NORMAL); } SDL_Surface *createTextSurface(const char *str, bool bold) { SDL_Color c = { cText, cText, cText, 255 }; applyFontStyle(bold); return TTF_RenderUTF8_Blended(font, str, c); } SDL_Surface *createTextSurface(const char *str, SDL_Color c, bool bold) { applyFontStyle(bold); return TTF_RenderUTF8_Blended(font, str, c); } /* Horizontally centered */ void blitTextSurf(SDL_Surface *surf, int x, int y, int alignW, SDL_Surface *txtSurf, Justification just) { SDL_Rect dstRect; dstRect.x = drawOff.x; dstRect.y = drawOff.y + y - txtSurf->h / 2; dstRect.w = txtSurf->w; dstRect.h = txtSurf->h; switch (just) { case Left: dstRect.x += x; break; case Center: dstRect.x += x - (txtSurf->w - alignW) / 2; break; } if (txtSurf->w <= alignW) { SDL_BlitSurface(txtSurf, 0, surf, &dstRect); } else { dstRect.w = alignW; dstRect.x = x; SDL_BlitScaled(txtSurf, 0, surf, &dstRect); } } void drawText(SDL_Surface *surf, const char *str, int x, int y, int alignW, Justification just, SDL_Color c, bool bold = false) { SDL_Surface *txt = createTextSurface(str, c, bold); blitTextSurf(surf, x, y, alignW, txt, just); SDL_FreeSurface(txt); } void drawText(SDL_Surface *surf, const char *str, int x, int y, int alignW, Justification just, bool bold = false) { SDL_Color c = { cText, cText, cText, 255 }; drawText(surf, str, x, y, alignW, just, c, bold); } void setupBindingData(const BDescVec &d) { size_t slotI[vButtonsN] = { 0 }; for (size_t i = 0; i < bWidgets.size(); ++i) for (size_t j = 0; j < 4; ++j) bWidgets[i].src[j].type = Invalid; for (size_t i = 0; i < d.size(); ++i) { const BindingDesc &desc = d[i]; const Input::ButtonCode trg = desc.target; size_t j; for (j = 0; j < vButtonsN; ++j) if (bWidgets[j].vb.code == trg) break; assert(j < vButtonsN); size_t &slot = slotI[j]; BindingWidget &w = bWidgets[j]; if (slot == 4) continue; w.src[slot++] = desc.src; } } void updateDuplicateStatus() { for (size_t i = 0; i < bWidgets.size(); ++i) for (size_t j = 0; j < 4; ++j) bWidgets[i].dupFlag[j] = false; bool haveDup = false; for (size_t i = 0; i < bWidgets.size(); ++i) { for (size_t j = 0; j < 4; ++j) { const SourceDesc &src = bWidgets[i].src[j]; if (src.type == Invalid) continue; for (size_t k = 0; k < bWidgets.size(); ++k) { if (k == i) continue; for (size_t l = 0; l < 4; ++l) { if (bWidgets[k].src[l] != src) continue; bWidgets[i].dupFlag[j] = true; bWidgets[k].dupFlag[l] = true; haveDup = true; } } } } dupWarnLabel.setVisible(haveDup); } void redraw() { fillSurface(winSurf, cBgNorm); for (size_t i = 0; i < widgets.size(); ++i) widgets[i]->draw(winSurf); if (state == AwaitingInput) { char buf[64]; snprintf(buf, sizeof(buf), "Press key or joystick button for \"%s\"", captureName); buf[sizeof(buf)-1] = '\0'; drawOff = Vec2i(); SDL_Surface *dark = createSurface(winSize.x, winSize.y); fillSurface(dark, 0); SDL_SetSurfaceAlphaMod(dark, 128); SDL_SetSurfaceBlendMode(dark, SDL_BLENDMODE_BLEND); SDL_Surface *txt = createTextSurface(buf, false); SDL_BlitSurface(dark, 0, winSurf, 0); SDL_Rect fill; fill.x = (winSize.x - txt->w - 20) / 2; fill.y = (winSize.y - txt->h - 20) / 2; fill.w = txt->w + 20; fill.h = txt->h + 20; fillRect(winSurf, cBgNorm, fill.x, fill.y, fill.w, fill.h); strokeRectInner(winSurf, cLine, fill.x, fill.y, fill.w, fill.h, 2); fill.x += 10; fill.y += 10; fill.w = txt->w; fill.h = txt->h; SDL_BlitSurface(txt, 0, winSurf, &fill); SDL_FreeSurface(txt); SDL_FreeSurface(dark); } SDL_UpdateWindowSurface(window); } Widget *findWidget(int x, int y) { Widget *w = 0; for (size_t i = 0; i < widgets.size(); ++i) if (widgets[i]->hit(x, y)) { w = widgets[i]; break; } return w; } void onClick(const SDL_MouseButtonEvent &e) { if (e.button != SDL_BUTTON_LEFT && e.button != SDL_BUTTON_RIGHT) return; if (state == AwaitingInput) { state = Idle; redraw(); return; } Widget *w = findWidget(e.x, e.y); if (w) w->click(e.x, e.y, e.button); } void onMotion(const SDL_MouseMotionEvent &e) { if (state == AwaitingInput) return; Widget *w = findWidget(e.x, e.y); if (w != hovered) { if (hovered) hovered->leave(); hovered = w; } if (hovered) hovered->motion(e.x, e.y); } bool onCaptureInputEvent(const SDL_Event &event) { assert(captureDesc); SourceDesc &desc = *captureDesc; switch (event.type) { case SDL_KEYDOWN: desc.type = Key; desc.d.scan = event.key.keysym.scancode; /* Special case aliases */ if (desc.d.scan == SDL_SCANCODE_RSHIFT) desc.d.scan = SDL_SCANCODE_LSHIFT; if (desc.d.scan == SDL_SCANCODE_KP_ENTER) desc.d.scan = SDL_SCANCODE_RETURN; break; case SDL_JOYBUTTONDOWN: desc.type = JButton; desc.d.jb = event.jbutton.button; break; case SDL_JOYHATMOTION: { int v = event.jhat.value; /* Only register if single directional input */ if (v != SDL_HAT_LEFT && v != SDL_HAT_RIGHT && v != SDL_HAT_UP && v != SDL_HAT_DOWN) return true; desc.type = JHat; desc.d.jh.hat = event.jhat.hat; desc.d.jh.pos = v; break; } case SDL_JOYAXISMOTION: { int v = event.jaxis.value; /* Only register if pushed halfway through */ if (v > -JAXIS_THRESHOLD && v < JAXIS_THRESHOLD) return true; desc.type = JAxis; desc.d.ja.axis = event.jaxis.axis; desc.d.ja.dir = v < 0 ? Negative : Positive; break; } default: return false; } captureDesc = 0; state = Idle; updateDuplicateStatus(); redraw(); return true; } void onBWidgetCellClicked(SourceDesc &desc, const char *str, uint8_t button) { if (state != Idle) return; if (button == SDL_BUTTON_LEFT) { captureDesc = &desc; captureName = str; state = AwaitingInput; } else /* e.button == SDL_BUTTON_RIGHT */ { /* Clear binding */ desc.type = Invalid; } updateDuplicateStatus(); redraw(); } void onResetToDefault() { setupBindingData(genDefaultBindings(rtData.config, rtData.gamecontroller)); updateDuplicateStatus(); redraw(); } void onAccept() { BDescVec binds; for (size_t i = 0; i < bWidgets.size(); ++i) bWidgets[i].appendBindings(binds); rtData.bindingUpdateMsg.post(binds); /* Store the key bindings to disk as well to prevent config loss */ storeBindings(binds, rtData.config); destroyReq = true; } void onCancel() { destroyReq = true; } }; Widget::Widget(SMP *p, const IntRect &rect) : p(p), rect(rect) {} bool Widget::hit(int x, int y) { return pointInRect(rect, x, y); } void Widget::draw(SDL_Surface *surf) { Vec2i prev = p->drawOff; p->drawOff = rect.pos(); drawHandler(surf); p->drawOff = prev; } void Widget::motion(int x, int y) { motionHandler(x - rect.x, y - rect.y); } void Widget::leave() { leaveHandler(); } void Widget::click(int x, int y, uint8_t button) { clickHandler(x - rect.x, y - rect.y, button); } /* Ratio of cell area to total widget width */ #define BW_CELL_R 0.75 void BindingWidget::drawHandler(SDL_Surface *surf) { const int cellW = (rect.w*BW_CELL_R) / 2; const int cellH = rect.h / 2; const int cellOffX = (1.0-BW_CELL_R) * rect.w; const int cellOff[] = { cellOffX, 1, cellOffX+cellW, 1, cellOffX, cellH, cellOffX+cellW, cellH }; const int lbOff[] = { 0, cellH / 2, cellW, cellH / 2, 0, cellH + cellH / 2, cellW, cellH + cellH / 2 }; /* Hovered cell background */ if (hoveredCell != -1) p->fillRect(surf, cBgDark, cellOff[hoveredCell*2], cellOff[hoveredCell*2+1], cellW, cellH); /* Frame */ p->strokeRectInner(surf, cLine, 0, 0, rect.w, rect.h, 2); /* Virtual button name */ p->drawText(surf, vb.str, 1, rect.h/2, cellOffX, Center, true); /* Cell frames */ p->strokeLineV(surf, cLine, cellOffX, 0, rect.h, 2); p->strokeLineV(surf, cLine, cellOffX+cellW, 0, rect.h, 2); p->strokeLineH(surf, cLine, cellOffX, cellH, cellW*2, 2); /* Draw binding labels */ for (size_t i = 0; i < 4; ++i) { std::string lb = sourceDescString(src[i]); if (lb.empty()) continue; const int x = lbOff[i*2+0]; const int y = lbOff[i*2+1]; p->drawText(surf, lb.c_str(), cellOffX+x+1, y, cellW-2, Center); } for (size_t i = 0; i < 4; ++i) { if (!dupFlag[i]) continue; p->strokeRectInner(surf, cellOff[i*2]+1, cellOff[i*2+1]+1, cellW-2, cellH-3, 1, 255, 0, 0); } } void BindingWidget::setHoveredCell(int cell) { if (hoveredCell == cell) return; hoveredCell = cell; p->redraw(); } void BindingWidget::motionHandler(int x, int y) { setHoveredCell(cellIndex(x, y)); } void BindingWidget::leaveHandler() { setHoveredCell(-1); } void BindingWidget::clickHandler(int x, int y, uint8_t button) { int cell = cellIndex(x, y); if (cell == -1) return; p->onBWidgetCellClicked(src[cell], vb.str, button); } int BindingWidget::cellIndex(int x, int y) const { const int cellW = (rect.w*BW_CELL_R) / 2; const int cellH = rect.h / 2; const int cellOff = (1.0-BW_CELL_R) * rect.w; if (x < cellOff) return -1; x -= cellOff; if (y < cellH) if (x < cellW) return 0; else return 1; else if (x < cellW) return 2; else return 3; return -1; } void BindingWidget::appendBindings(BDescVec &d) const { for (size_t i = 0; i < 4; ++i) { if (src[i].type == Invalid) continue; BindingDesc desc; desc.src = src[i]; desc.target = vb.code; d.push_back(desc); } } void Button::setHovered(bool val) { if (hovered == val) return; hovered = val; p->redraw(); } void Button::drawHandler(SDL_Surface *surf) { if (hovered) p->fillRect(surf, cBgDark, 0, 0, rect.w, rect.h); p->strokeRectInner(surf, cLine, 0, 0, rect.w, rect.h, 2); p->drawText(surf, str, 0, rect.h/2, rect.w, Center); } void Button::motionHandler(int, int) { setHovered(true); } void Button::leaveHandler() { setHovered(false); } void Button::clickHandler(int, int, uint8_t button) { if (button != SDL_BUTTON_LEFT) return; (p->*cb)(); } void Label::setVisible(bool val) { if (visible == val) return; visible = val; p->redraw(); } void Label::drawHandler(SDL_Surface *surf) { if (visible) p->drawText(surf, str, 0, rect.h/2, rect.w, Left, c); } SettingsMenu::SettingsMenu(RGSSThreadData &rtData) { p = new SettingsMenuPrivate(rtData); p->state = Idle; p->hasFocus = false; p->destroyReq = false; p->window = SDL_CreateWindow("Key bindings", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, winSize.x, winSize.y, SDL_WINDOW_INPUT_FOCUS); p->winSurf = SDL_GetWindowSurface(p->window); p->winID = SDL_GetWindowID(p->window); p->font = SharedFontState::openBundled(fontSize); p->rgb = p->winSurf->format; const size_t layoutW = 4; const size_t layoutH = 3; assert(layoutW*layoutH == vButtonsN); const int bWidgetW = winSize.x / layoutH; const int bWidgetH = 64; const int bWidgetY = winSize.y - layoutW*bWidgetH - 48; for (int y = 0; y < 4; ++y) for (int x = 0; x < 3; ++x) { int i = y*3+x; BindingWidget w(i, p, IntRect(x*bWidgetW, bWidgetY+y*bWidgetH, bWidgetW, bWidgetH)); p->bWidgets.push_back(w); } for (size_t i = 0; i< p->bWidgets.size(); ++i) p->widgets.push_back(&p->bWidgets[i]); BDescVec binds; rtData.bindingUpdateMsg.get(binds); p->setupBindingData(binds); /* Buttons */ const int buttonH = 32; const int buttonY = winSize.y - buttonH - 8; IntRect btRects[] = { IntRect(16, buttonY, 112, buttonH), IntRect(winSize.x-16-64*2-8, buttonY, 64, buttonH), IntRect(winSize.x-16-64, buttonY, 64, buttonH) }; p->buttons.push_back(Button(p, btRects[0], "Reset defaults", &SMP::onResetToDefault)); p->buttons.push_back(Button(p, btRects[1], "Cancel", &SMP::onCancel)); p->buttons.push_back(Button(p, btRects[2], "Accept", &SMP::onAccept)); for (size_t i = 0; i< p->buttons.size(); ++i) p->widgets.push_back(&p->buttons[i]); /* Labels */ const char *info = "Use left click to bind a slot, right click to clear its binding"; p->infoLabel = Label(p, IntRect(16, 6, winSize.x, 16), info, cText, cText, cText); const char *warn = "Warning: Same physical key bound to multiple slots"; p->dupWarnLabel = Label(p, IntRect(16, 26, winSize.x, 16), warn, 255, 0, 0); p->widgets.push_back(&p->infoLabel); p->widgets.push_back(&p->dupWarnLabel); p->hovered = 0; p->captureDesc = 0; p->captureName = 0; p->updateDuplicateStatus(); p->redraw(); } SettingsMenu::~SettingsMenu() { TTF_CloseFont(p->font); SDL_DestroyWindow(p->window); delete p; } bool SettingsMenu::onEvent(const SDL_Event &event) { /* First, check whether this event is for us */ switch (event.type) { case SDL_WINDOWEVENT : case SDL_MOUSEBUTTONDOWN : case SDL_MOUSEBUTTONUP : case SDL_MOUSEMOTION : case SDL_KEYDOWN : case SDL_KEYUP : /* We can do this because windowID has the same * struct offset in all these event types */ if (event.window.windowID != p->winID) return false; break; case SDL_JOYBUTTONDOWN : case SDL_JOYBUTTONUP : case SDL_JOYHATMOTION : case SDL_JOYAXISMOTION : if (!p->hasFocus) return false; break; /* Don't try to handle something we don't understand */ default: return false; } /* Now process it.. */ switch (event.type) { /* Ignore these event */ case SDL_MOUSEBUTTONUP : case SDL_KEYUP : return true; case SDL_WINDOWEVENT : switch (event.window.event) { case SDL_WINDOWEVENT_SHOWN : // SDL is bugged and doesn't give us a first FOCUS_GAINED event case SDL_WINDOWEVENT_FOCUS_GAINED : p->hasFocus = true; break; case SDL_WINDOWEVENT_FOCUS_LOST : p->hasFocus = false; break; case SDL_WINDOWEVENT_EXPOSED : SDL_UpdateWindowSurface(p->window); break; case SDL_WINDOWEVENT_LEAVE: if (p->hovered) { p->hovered->leave(); p->hovered = 0; } break; case SDL_WINDOWEVENT_CLOSE: p->onCancel(); } return true; case SDL_MOUSEMOTION: p->onMotion(event.motion); return true; case SDL_KEYDOWN: if (p->state != AwaitingInput) { if (event.key.keysym.sym == SDLK_RETURN) p->onAccept(); else if (event.key.keysym.sym == SDLK_ESCAPE) p->onCancel(); return true; } /* Don't let the user bind keys that trigger * mkxp functions */ switch(event.key.keysym.scancode) { case SDL_SCANCODE_F1: case SDL_SCANCODE_F2: case SDL_SCANCODE_F12: return true; default: break; } case SDL_JOYBUTTONDOWN: case SDL_JOYHATMOTION: case SDL_JOYAXISMOTION: if (p->state != AwaitingInput) return true; break; case SDL_MOUSEBUTTONDOWN: p->onClick(event.button); return true; default: return true; } if (p->state == AwaitingInput) return p->onCaptureInputEvent(event); return true; } void SettingsMenu::raise() { SDL_RaiseWindow(p->window); } bool SettingsMenu::destroyReq() const { return p->destroyReq; }