Merge branch 'master' (early part) into medusa
7
CHANGES
|
@ -36,6 +36,13 @@ Misc:
|
|||
0.10.0: (Future)
|
||||
Features:
|
||||
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
|
||||
- Cheat code support in homebrew ports
|
||||
Emulation fixes:
|
||||
- GBA Video: Revert scanline latching changes (fixes mgba.io/i/2153, mgba.io/i/2149)
|
||||
Other fixes:
|
||||
- Core: Fix memory leak in opening games from the library
|
||||
- Qt: Fix infrequent deadlock when using sync to video
|
||||
- Qt: Fix applying savetype-only overrides
|
||||
Misc:
|
||||
- Qt: Rearrange menus some
|
||||
|
||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
@ -14,6 +14,9 @@ CXX_GUARD_START
|
|||
#include <mgba/core/input.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
#define MAX_KEYBOARD_LEN 256
|
||||
#define MAX_KEYBOARD_TITLE_LEN 128
|
||||
|
||||
struct GUIFont;
|
||||
|
||||
enum GUIInput {
|
||||
|
@ -40,6 +43,11 @@ enum GUICursorState {
|
|||
GUI_CURSOR_DRAGGING
|
||||
};
|
||||
|
||||
enum GUIKeyboardStatus {
|
||||
GUI_KEYBOARD_DONE = 0,
|
||||
GUI_KEYBOARD_CANCEL,
|
||||
};
|
||||
|
||||
enum {
|
||||
BATTERY_EMPTY = 0,
|
||||
BATTERY_LOW = 25,
|
||||
|
@ -57,6 +65,13 @@ struct GUIBackground {
|
|||
void (*draw)(struct GUIBackground*, void* context);
|
||||
};
|
||||
|
||||
struct GUIKeyboardParams {
|
||||
char title[MAX_KEYBOARD_TITLE_LEN];
|
||||
char result[MAX_KEYBOARD_LEN];
|
||||
size_t maxLen;
|
||||
bool multiline;
|
||||
};
|
||||
|
||||
struct GUIParams {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
@ -70,6 +85,7 @@ struct GUIParams {
|
|||
int (*batteryState)(void);
|
||||
void (*guiPrepare)(void);
|
||||
void (*guiFinish)(void);
|
||||
enum GUIKeyboardStatus (*getText)(struct GUIKeyboardParams*);
|
||||
|
||||
// State
|
||||
struct mInputMap keyMap;
|
||||
|
@ -87,6 +103,8 @@ void GUIPollInput(struct GUIParams* params, uint32_t* newInput, uint32_t* heldIn
|
|||
enum GUICursorState GUIPollCursor(struct GUIParams* params, unsigned* x, unsigned* y);
|
||||
void GUIInvalidateKeys(struct GUIParams* params);
|
||||
|
||||
void GUIKeyboardParamsInit(struct GUIKeyboardParams*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@ bool endswith(const char* restrict s1, const char* restrict end);
|
|||
bool startswith(const char* restrict s1, const char* restrict start);
|
||||
|
||||
size_t toUtf8(uint32_t unichar, char* buffer);
|
||||
size_t toUtf16(uint32_t unichar, uint16_t* buffer);
|
||||
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length);
|
||||
char* utf16to8(const uint16_t* utf16, size_t length);
|
||||
uint32_t utf8Char(const char** unicode, size_t* length);
|
||||
|
|
|
@ -456,6 +456,11 @@ struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryE
|
|||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
|
||||
struct mLibraryEntry* e = mLibraryListingGetPointer(&entries, i);
|
||||
mLibraryEntryFree(e);
|
||||
}
|
||||
mLibraryListingDeinit(&entries);
|
||||
return vf;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ set(SOURCE_FILES
|
|||
video-logger.c)
|
||||
|
||||
set(GUI_FILES
|
||||
gui/cheats.c
|
||||
gui/gui-config.c
|
||||
gui/gui-runner.c
|
||||
gui/remap.c)
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/* Copyright (c) 2013-2021 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gui-config.h"
|
||||
|
||||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include "feature/gui/gui-runner.h"
|
||||
#include <mgba-util/gui/menu.h>
|
||||
|
||||
enum mGUICheatAction {
|
||||
CHEAT_ADD_LINE = 1,
|
||||
CHEAT_RENAME,
|
||||
CHEAT_DELETE,
|
||||
};
|
||||
|
||||
static const char* const offOn[] = { "Off", "On" };
|
||||
|
||||
static void mGUIShowCheatSet(struct mGUIRunner* runner, struct mCheatDevice* device, struct mCheatSet* set) {
|
||||
char cheatName[64];
|
||||
snprintf(cheatName, sizeof(cheatName), "Edit cheat: %s", set->name);
|
||||
struct GUIMenu menu = {
|
||||
.title = cheatName,
|
||||
.index = 0,
|
||||
.background = &runner->background.d
|
||||
};
|
||||
GUIMenuItemListInit(&menu.items, 0);
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Enable",
|
||||
.state = set->enabled,
|
||||
.validStates = offOn,
|
||||
.nStates = 2
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Add line",
|
||||
.data = (void*) CHEAT_ADD_LINE,
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Rename",
|
||||
.data = (void*) CHEAT_RENAME,
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Delete",
|
||||
.data = (void*) CHEAT_DELETE,
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Back",
|
||||
.data = 0,
|
||||
};
|
||||
|
||||
while (true) {
|
||||
struct GUIKeyboardParams keyboard;
|
||||
GUIKeyboardParamsInit(&keyboard);
|
||||
struct GUIMenuItem* item;
|
||||
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &menu, &item);
|
||||
set->enabled = GUIMenuItemListGetPointer(&menu.items, 0)->state;
|
||||
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
|
||||
break;
|
||||
}
|
||||
|
||||
enum mGUICheatAction action = (enum mGUICheatAction) item->data;
|
||||
switch (action) {
|
||||
case CHEAT_ADD_LINE:
|
||||
strlcpy(keyboard.title, "Add line", sizeof(keyboard.title));
|
||||
keyboard.maxLen = 12;
|
||||
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
|
||||
mCheatAddLine(set, keyboard.result, 0);
|
||||
}
|
||||
break;
|
||||
case CHEAT_RENAME:
|
||||
strlcpy(keyboard.title, "Rename cheat", sizeof(keyboard.title));
|
||||
strlcpy(keyboard.result, set->name, sizeof(keyboard.result));
|
||||
keyboard.maxLen = 50;
|
||||
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
|
||||
mCheatSetRename(set, keyboard.result);
|
||||
snprintf(cheatName, sizeof(cheatName), "Edit cheat: %s", set->name);
|
||||
}
|
||||
break;
|
||||
case CHEAT_DELETE:
|
||||
mCheatRemoveSet(device, set);
|
||||
break;
|
||||
}
|
||||
|
||||
if (action == CHEAT_DELETE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
GUIMenuItemListDeinit(&menu.items);
|
||||
}
|
||||
|
||||
void mGUIShowCheats(struct mGUIRunner* runner) {
|
||||
struct mCheatDevice* device = runner->core->cheatDevice(runner->core);
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
struct GUIMenu menu = {
|
||||
.title = "Cheats",
|
||||
.index = 0,
|
||||
.background = &runner->background.d
|
||||
};
|
||||
GUIMenuItemListInit(&menu.items, 0);
|
||||
|
||||
while (true) {
|
||||
size_t i;
|
||||
for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
|
||||
struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = set->name,
|
||||
.data = set,
|
||||
.state = set->enabled,
|
||||
.validStates = offOn,
|
||||
.nStates = 2
|
||||
};
|
||||
}
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Add new cheat set",
|
||||
.data = 0,
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Back",
|
||||
.data = (void*) -1,
|
||||
};
|
||||
|
||||
struct GUIMenuItem* item;
|
||||
enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &menu, &item);
|
||||
for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
|
||||
struct mCheatSet* set = *mCheatSetsGetPointer(&device->cheats, i);
|
||||
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu.items, i);
|
||||
set->enabled = item->state;
|
||||
}
|
||||
|
||||
if (reason != GUI_MENU_EXIT_ACCEPT || item->data == (void*) -1) {
|
||||
break;
|
||||
}
|
||||
struct mCheatSet* set = NULL;
|
||||
if (!item->data) {
|
||||
struct GUIKeyboardParams keyboard;
|
||||
GUIKeyboardParamsInit(&keyboard);
|
||||
keyboard.maxLen = 50;
|
||||
strlcpy(keyboard.title, "Cheat name", sizeof(keyboard.title));
|
||||
strlcpy(keyboard.result, "New cheat", sizeof(keyboard.result));
|
||||
if (runner->params.getText(&keyboard) == GUI_KEYBOARD_DONE) {
|
||||
set = device->createSet(device, keyboard.result);
|
||||
mCheatAddSet(device, set);
|
||||
}
|
||||
} else {
|
||||
set = item->data;
|
||||
}
|
||||
if (set) {
|
||||
mGUIShowCheatSet(runner, device, set);
|
||||
}
|
||||
GUIMenuItemListClear(&menu.items);
|
||||
}
|
||||
GUIMenuItemListDeinit(&menu.items);
|
||||
mCheatAutosave(device);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* Copyright (c) 2013-2021 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef GUI_CHEATS_H
|
||||
#define GUI_CHEATS_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct mGUIRunner;
|
||||
void mGUIShowCheats(struct mGUIRunner* runner);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -380,4 +380,5 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
|||
}
|
||||
}
|
||||
}
|
||||
GUIMenuItemListDeinit(&menu.items);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include "feature/gui/gui-config.h"
|
||||
#include "feature/gui/cheats.h"
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#include <mgba/gba/interface.h>
|
||||
|
@ -35,6 +36,7 @@ enum {
|
|||
RUNNER_SCREENSHOT,
|
||||
RUNNER_CONFIG,
|
||||
RUNNER_RESET,
|
||||
RUNNER_CHEATS,
|
||||
RUNNER_COMMAND_MASK = 0xFFFF
|
||||
};
|
||||
|
||||
|
@ -333,7 +335,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
};
|
||||
GUIMenuItemListInit(&pauseMenu.items, 0);
|
||||
GUIMenuItemListInit(&stateSaveMenu.items, 9);
|
||||
GUIMenuItemListInit(&stateLoadMenu.items, 9);
|
||||
GUIMenuItemListInit(&stateLoadMenu.items, 10);
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = (void*) RUNNER_CONTINUE };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
|
||||
|
@ -360,6 +362,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
|
||||
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT };
|
||||
if (runner->params.getText) {
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Cheats", .data = (void*) RUNNER_CHEATS };
|
||||
}
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Reset game", .data = (void*) RUNNER_RESET };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = (void*) RUNNER_EXIT };
|
||||
|
@ -616,6 +621,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
case RUNNER_CONFIG:
|
||||
mGUIShowConfig(runner, runner->configExtra, runner->nConfigExtra);
|
||||
break;
|
||||
case RUNNER_CHEATS:
|
||||
mGUIShowCheats(runner);
|
||||
break;
|
||||
case RUNNER_CONTINUE:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -84,4 +84,5 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
|
|||
// TODO: Open remap menu
|
||||
}
|
||||
}
|
||||
GUIMenuItemListDeinit(&menu.items);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ static const struct GBACartridgeOverride _overrides[] = {
|
|||
{ "AC8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
|
||||
{ "AC8P", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
|
||||
|
||||
// DigiCommunication Nyo - Datou! Black Gemagema Dan
|
||||
{ "BDKJ", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
|
||||
|
||||
// Dragon Ball Z - The Legacy of Goku
|
||||
{ "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false },
|
||||
|
||||
|
@ -50,6 +53,7 @@ static const struct GBACartridgeOverride _overrides[] = {
|
|||
// Drill Dozer
|
||||
{ "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
|
||||
{ "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
|
||||
{ "V49P", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE, false },
|
||||
|
||||
// e-Reader
|
||||
{ "PEAJ", SAVEDATA_FLASH1M, HW_EREADER, IDLE_LOOP_NONE },
|
||||
|
|
|
@ -218,7 +218,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
PREPARE_OBJWIN;
|
||||
|
||||
#define TEST_LAYER_ENABLED(X) \
|
||||
(softwareRenderer->bg[X].enabled == 3 && \
|
||||
(softwareRenderer->bg[X].enabled == 4 && \
|
||||
(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
|
||||
(GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \
|
||||
softwareRenderer->bg[X].priority == priority)
|
||||
|
|
|
@ -699,19 +699,19 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
|
||||
}
|
||||
|
||||
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) {
|
||||
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) {
|
||||
++softwareRenderer->bg[0].enabled;
|
||||
DIRTY_SCANLINE(softwareRenderer, y);
|
||||
}
|
||||
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) {
|
||||
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) {
|
||||
++softwareRenderer->bg[1].enabled;
|
||||
DIRTY_SCANLINE(softwareRenderer, y);
|
||||
}
|
||||
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) {
|
||||
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) {
|
||||
++softwareRenderer->bg[2].enabled;
|
||||
DIRTY_SCANLINE(softwareRenderer, y);
|
||||
}
|
||||
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) {
|
||||
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) {
|
||||
++softwareRenderer->bg[3].enabled;
|
||||
DIRTY_SCANLINE(softwareRenderer, y);
|
||||
}
|
||||
|
@ -759,16 +759,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere
|
|||
softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
|
||||
|
||||
if (softwareRenderer->bg[0].enabled > 0) {
|
||||
softwareRenderer->bg[0].enabled = 3;
|
||||
softwareRenderer->bg[0].enabled = 4;
|
||||
}
|
||||
if (softwareRenderer->bg[1].enabled > 0) {
|
||||
softwareRenderer->bg[1].enabled = 3;
|
||||
softwareRenderer->bg[1].enabled = 4;
|
||||
}
|
||||
if (softwareRenderer->bg[2].enabled > 0) {
|
||||
softwareRenderer->bg[2].enabled = 3;
|
||||
softwareRenderer->bg[2].enabled = 4;
|
||||
}
|
||||
if (softwareRenderer->bg[3].enabled > 0) {
|
||||
softwareRenderer->bg[3].enabled = 3;
|
||||
softwareRenderer->bg[3].enabled = 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -795,7 +795,7 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac
|
|||
} else if (!wasActive && active) {
|
||||
if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
|
||||
// TODO: Investigate in more depth how switching background works in different modes
|
||||
renderer->bg[bg].enabled = 3;
|
||||
renderer->bg[bg].enabled = 4;
|
||||
} else {
|
||||
renderer->bg[bg].enabled = 1;
|
||||
}
|
||||
|
|
|
@ -182,9 +182,6 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
|
||||
|
||||
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
|
||||
if (video->frameskipCounter <= 0) {
|
||||
video->renderer->drawScanline(video->renderer, video->vcount);
|
||||
}
|
||||
video->shouldStall = 1;
|
||||
}
|
||||
|
||||
|
@ -236,6 +233,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
// Begin Hblank
|
||||
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
||||
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
|
||||
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
|
||||
video->renderer->drawScanline(video->renderer, video->vcount);
|
||||
}
|
||||
|
||||
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
|
||||
GBADMARunHblank(video->p, -cyclesLate);
|
||||
|
|
|
@ -797,6 +797,22 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig
|
|||
}
|
||||
}
|
||||
|
||||
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
|
||||
SwkbdState swkbd;
|
||||
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, keyboard->maxLen);
|
||||
swkbdSetInitialText(&swkbd, keyboard->result);
|
||||
if (keyboard->multiline) {
|
||||
swkbdSetFeatures(&swkbd, SWKBD_MULTILINE);
|
||||
}
|
||||
|
||||
SwkbdButton button = swkbdInputText(&swkbd, keyboard->result, sizeof( keyboard->result));
|
||||
if (button == SWKBD_BUTTON_CONFIRM) {
|
||||
return GUI_KEYBOARD_DONE;
|
||||
} else {
|
||||
return GUI_KEYBOARD_CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
THREAD_ENTRY _core2Test(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
@ -893,6 +909,7 @@ int main() {
|
|||
_pollInput, _pollCursor,
|
||||
_batteryState,
|
||||
_guiPrepare, _guiFinish,
|
||||
_keyboardRun,
|
||||
},
|
||||
.keySources = (struct GUIInputKeys[]) {
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
|
|||
-lSceCtrl_stub
|
||||
-lSceDisplay_stub
|
||||
-lSceGxm_stub
|
||||
-lSceIme_stub
|
||||
-lSceMotion_stub
|
||||
-lScePgf_stub
|
||||
-lScePhotoExport_stub
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
#include <mgba-util/gui/font.h>
|
||||
#include <mgba-util/gui/file-select.h>
|
||||
#include <mgba-util/gui/menu.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#include <psp2/apputil.h>
|
||||
#include <psp2/ctrl.h>
|
||||
#include <psp2/display.h>
|
||||
#include <psp2/ime_dialog.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <psp2/power.h>
|
||||
|
@ -36,6 +38,7 @@ static void _drawStart(void) {
|
|||
|
||||
static void _drawEnd(void) {
|
||||
vita2d_end_drawing();
|
||||
vita2d_common_dialog_update();
|
||||
vita2d_swap_buffers();
|
||||
}
|
||||
|
||||
|
@ -81,6 +84,66 @@ static int _batteryState(void) {
|
|||
return state | charge;
|
||||
}
|
||||
|
||||
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
|
||||
SceImeDialogParam params;
|
||||
sceImeDialogParamInit(¶ms);
|
||||
params.supportedLanguages = 0x0001FFFF;
|
||||
params.languagesForced = SCE_TRUE;
|
||||
params.type = SCE_IME_TYPE_DEFAULT;
|
||||
params.option = 0;
|
||||
if (keyboard->multiline) {
|
||||
params.option = SCE_IME_OPTION_MULTILINE;
|
||||
}
|
||||
params.dialogMode = SCE_IME_DIALOG_DIALOG_MODE_WITH_CANCEL;
|
||||
params.maxTextLength = keyboard->maxLen;
|
||||
params.title = calloc(sizeof(SceWChar16), MAX_KEYBOARD_TITLE_LEN + 1);
|
||||
params.inputTextBuffer = calloc(sizeof(SceWChar16), keyboard->maxLen + 1);
|
||||
params.initialText = calloc(sizeof(SceWChar16), keyboard->maxLen + 1);
|
||||
|
||||
uint16_t* utf16Buffer = params.initialText;
|
||||
char* utf8Buffer = keyboard->result;
|
||||
size_t i = keyboard->maxLen;
|
||||
while (i > 0 && *utf8Buffer) {
|
||||
uint32_t unichar = utf8Char((const char**) &utf8Buffer, &i);
|
||||
utf16Buffer += toUtf16(unichar, utf16Buffer);
|
||||
}
|
||||
|
||||
utf16Buffer = params.title;
|
||||
utf8Buffer = keyboard->title;
|
||||
i = MAX_KEYBOARD_TITLE_LEN;
|
||||
while (i > 0 && *utf8Buffer) {
|
||||
uint32_t unichar = utf8Char((const char**) &utf8Buffer, &i);
|
||||
utf16Buffer += toUtf16(unichar, utf16Buffer);
|
||||
}
|
||||
|
||||
sceImeDialogInit(¶ms);
|
||||
SceCommonDialogStatus status = SCE_COMMON_DIALOG_STATUS_RUNNING;
|
||||
while (status == SCE_COMMON_DIALOG_STATUS_RUNNING) {
|
||||
_drawStart();
|
||||
status = sceImeDialogGetStatus();
|
||||
_drawEnd();
|
||||
}
|
||||
|
||||
SceImeDialogResult result;
|
||||
memset(&result, 0, sizeof(SceImeDialogResult));
|
||||
sceImeDialogGetResult(&result);
|
||||
sceImeDialogTerm();
|
||||
|
||||
utf16Buffer = params.inputTextBuffer;
|
||||
utf8Buffer = keyboard->result;
|
||||
i = keyboard->maxLen;
|
||||
while (i > 0 && *utf16Buffer) {
|
||||
uint32_t unichar = utf16Char((const uint16_t**) &utf16Buffer, &i);
|
||||
utf8Buffer += toUtf8(unichar, utf8Buffer);
|
||||
}
|
||||
utf8Buffer[0] = 0;
|
||||
|
||||
free(params.initialText);
|
||||
free(params.inputTextBuffer);
|
||||
|
||||
return result.button == SCE_IME_DIALOG_BUTTON_ENTER ? GUI_KEYBOARD_DONE : GUI_KEYBOARD_CANCEL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
vita2d_init();
|
||||
struct GUIFont* font = GUIFontCreate();
|
||||
|
@ -92,6 +155,7 @@ int main() {
|
|||
_pollInput, _pollCursor,
|
||||
_batteryState,
|
||||
0, 0,
|
||||
_keyboardRun,
|
||||
},
|
||||
.configExtra = (struct GUIMenuItem[]) {
|
||||
{
|
||||
|
@ -167,6 +231,7 @@ int main() {
|
|||
sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE);
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_PHOTO_EXPORT);
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_APPUTIL);
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_IME);
|
||||
|
||||
mGUIInit(&runner, "psvita");
|
||||
|
||||
|
|
|
@ -161,7 +161,6 @@ void DisplayGL::stopDrawing() {
|
|||
void DisplayGL::pauseDrawing() {
|
||||
if (m_hasStarted) {
|
||||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection);
|
||||
#ifndef Q_OS_MAC
|
||||
setUpdatesEnabled(true);
|
||||
|
@ -172,7 +171,6 @@ void DisplayGL::pauseDrawing() {
|
|||
void DisplayGL::unpauseDrawing() {
|
||||
if (m_hasStarted) {
|
||||
m_isDrawing = true;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
|
||||
#ifndef Q_OS_MAC
|
||||
setUpdatesEnabled(false);
|
||||
|
@ -464,11 +462,6 @@ void PainterGL::draw() {
|
|||
performDraw();
|
||||
m_backend->swap(m_backend);
|
||||
}
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (!m_queue.isEmpty()) {
|
||||
QTimer::singleShot(1, this, &PainterGL::draw);
|
||||
}
|
||||
}
|
||||
|
||||
void PainterGL::forceDraw() {
|
||||
|
|
|
@ -146,6 +146,9 @@ void OverrideView::updateOverrides() {
|
|||
gba->override.vbaBugCompat = false;
|
||||
gba->vbaBugCompatSet = false;
|
||||
|
||||
if (gba->override.savetype != SAVEDATA_AUTODETECT) {
|
||||
hasOverride = true;
|
||||
}
|
||||
if (!m_ui.hwAutodetect->isChecked()) {
|
||||
hasOverride = true;
|
||||
gba->override.hardware = HW_NONE;
|
||||
|
|
|
@ -13,29 +13,35 @@
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
LibraryEntry::LibraryEntry(mLibraryEntry* entry)
|
||||
: entry(entry)
|
||||
, m_fullpath(QString("%1/%2").arg(entry->base, entry->filename))
|
||||
LibraryEntry::LibraryEntry(const mLibraryEntry* entry)
|
||||
: base(entry->base)
|
||||
, filename(entry->filename)
|
||||
, fullpath(QString("%1/%2").arg(entry->base, entry->filename))
|
||||
, title(entry->title)
|
||||
, internalTitle(entry->internalTitle)
|
||||
, internalCode(entry->internalCode)
|
||||
, platform(entry->platform)
|
||||
, filesize(entry->filesize)
|
||||
, crc32(entry->crc32)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractGameList::addEntries(QList<LibraryEntryRef> items) {
|
||||
for (LibraryEntryRef o : items) {
|
||||
addEntry(o);
|
||||
}
|
||||
void AbstractGameList::addEntry(const LibraryEntry& item) {
|
||||
addEntries({item});
|
||||
}
|
||||
void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
|
||||
for (LibraryEntryRef o : items) {
|
||||
removeEntry(o);
|
||||
}
|
||||
|
||||
void AbstractGameList::updateEntry(const LibraryEntry& item) {
|
||||
updateEntries({item});
|
||||
}
|
||||
|
||||
void AbstractGameList::removeEntry(const QString& item) {
|
||||
removeEntries({item});
|
||||
}
|
||||
|
||||
LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
|
||||
: QStackedWidget(parent)
|
||||
, m_config(config)
|
||||
{
|
||||
mLibraryListingInit(&m_listing, 0);
|
||||
|
||||
if (!path.isNull()) {
|
||||
// This can return NULL if the library is already open
|
||||
m_library = std::shared_ptr<mLibrary>(mLibraryLoad(path.toUtf8().constData()), mLibraryDestroy);
|
||||
|
@ -58,8 +64,6 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
|||
}
|
||||
|
||||
LibraryController::~LibraryController() {
|
||||
freeLibrary();
|
||||
mLibraryListingDeinit(&m_listing);
|
||||
}
|
||||
|
||||
void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
||||
|
@ -74,45 +78,53 @@ void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
|||
} else {
|
||||
newCurrentList = m_libraryGrid.get();
|
||||
}
|
||||
newCurrentList->selectEntry(selectedEntry());
|
||||
newCurrentList->selectEntry(selectedEntry().fullpath);
|
||||
newCurrentList->setViewStyle(newStyle);
|
||||
setCurrentWidget(newCurrentList->widget());
|
||||
m_currentList = newCurrentList;
|
||||
}
|
||||
|
||||
void LibraryController::selectEntry(LibraryEntryRef entry) {
|
||||
void LibraryController::selectEntry(const QString& fullpath) {
|
||||
if (!m_currentList) {
|
||||
return;
|
||||
}
|
||||
m_currentList->selectEntry(entry);
|
||||
m_currentList->selectEntry(fullpath);
|
||||
}
|
||||
|
||||
LibraryEntryRef LibraryController::selectedEntry() {
|
||||
LibraryEntry LibraryController::selectedEntry() {
|
||||
if (!m_currentList) {
|
||||
return LibraryEntryRef();
|
||||
return {};
|
||||
}
|
||||
return m_currentList->selectedEntry();
|
||||
return m_entries.value(m_currentList->selectedEntry());
|
||||
}
|
||||
|
||||
VFile* LibraryController::selectedVFile() {
|
||||
LibraryEntryRef entry = selectedEntry();
|
||||
if (entry) {
|
||||
return mLibraryOpenVFile(m_library.get(), entry->entry);
|
||||
LibraryEntry entry = selectedEntry();
|
||||
if (!entry.isNull()) {
|
||||
mLibraryEntry libentry = {0};
|
||||
QByteArray baseUtf8(entry.base.toUtf8());
|
||||
QByteArray filenameUtf8(entry.filename.toUtf8());
|
||||
libentry.base = baseUtf8.constData();
|
||||
libentry.filename = filenameUtf8.constData();
|
||||
return mLibraryOpenVFile(m_library.get(), &libentry);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QPair<QString, QString> LibraryController::selectedPath() {
|
||||
LibraryEntryRef e = selectedEntry();
|
||||
return e ? qMakePair(e->base(), e->filename()) : qMakePair<QString, QString>("", "");
|
||||
LibraryEntry entry = selectedEntry();
|
||||
if (!entry.isNull()) {
|
||||
return qMakePair(QString(entry.base), QString(entry.filename));
|
||||
} else {
|
||||
return qMakePair(QString(), QString());
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryController::addDirectory(const QString& dir, bool recursive) {
|
||||
// The worker thread temporarily owns the library
|
||||
std::shared_ptr<mLibrary> library = m_library;
|
||||
m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir, recursive), this, [this, library]() {
|
||||
m_libraryJob = -1;
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
@ -133,38 +145,47 @@ void LibraryController::refresh() {
|
|||
|
||||
setDisabled(true);
|
||||
|
||||
QStringList allEntries;
|
||||
QList<LibraryEntryRef> newEntries;
|
||||
QHash<QString, LibraryEntry> removedEntries = m_entries;
|
||||
QHash<QString, LibraryEntry> updatedEntries;
|
||||
QList<LibraryEntry> newEntries;
|
||||
|
||||
freeLibrary();
|
||||
mLibraryGetEntries(m_library.get(), &m_listing, 0, 0, nullptr);
|
||||
for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
|
||||
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
|
||||
QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
|
||||
if (m_entries.contains(fullpath)) {
|
||||
m_entries.value(fullpath)->entry = entry;
|
||||
mLibraryListing listing;
|
||||
mLibraryListingInit(&listing, 0);
|
||||
mLibraryGetEntries(m_library.get(), &listing, 0, 0, nullptr);
|
||||
for (size_t i = 0; i < mLibraryListingSize(&listing); i++) {
|
||||
LibraryEntry entry = mLibraryListingGetConstPointer(&listing, i);
|
||||
if (!m_entries.contains(entry.fullpath)) {
|
||||
newEntries.append(entry);
|
||||
} else {
|
||||
LibraryEntryRef libentry = std::make_shared<LibraryEntry>(entry);
|
||||
m_entries.insert(fullpath, libentry);
|
||||
newEntries.append(libentry);
|
||||
updatedEntries[entry.fullpath] = entry;
|
||||
}
|
||||
allEntries.append(fullpath);
|
||||
m_entries[entry.fullpath] = entry;
|
||||
removedEntries.remove(entry.fullpath);
|
||||
}
|
||||
|
||||
// Check for entries that were removed
|
||||
QList<LibraryEntryRef> removedEntries;
|
||||
for (QString& path : m_entries.keys()) {
|
||||
if (!allEntries.contains(path)) {
|
||||
removedEntries.append(m_entries.value(path));
|
||||
m_entries.remove(path);
|
||||
}
|
||||
for (QString& path : removedEntries.keys()) {
|
||||
m_entries.remove(path);
|
||||
}
|
||||
|
||||
m_libraryTree->addEntries(newEntries);
|
||||
m_libraryGrid->addEntries(newEntries);
|
||||
if (!removedEntries.size() && !newEntries.size()) {
|
||||
m_libraryTree->updateEntries(updatedEntries.values());
|
||||
m_libraryGrid->updateEntries(updatedEntries.values());
|
||||
} else if (!updatedEntries.size()) {
|
||||
m_libraryTree->removeEntries(removedEntries.keys());
|
||||
m_libraryGrid->removeEntries(removedEntries.keys());
|
||||
|
||||
m_libraryTree->removeEntries(removedEntries);
|
||||
m_libraryGrid->removeEntries(removedEntries);
|
||||
m_libraryTree->addEntries(newEntries);
|
||||
m_libraryGrid->addEntries(newEntries);
|
||||
} else {
|
||||
m_libraryTree->resetEntries(m_entries.values());
|
||||
m_libraryGrid->resetEntries(m_entries.values());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mLibraryListingSize(&listing); ++i) {
|
||||
mLibraryEntryFree(mLibraryListingGetPointer(&listing, i));
|
||||
}
|
||||
mLibraryListingDeinit(&listing);
|
||||
|
||||
setDisabled(false);
|
||||
selectLastBootedGame();
|
||||
|
@ -177,19 +198,14 @@ void LibraryController::selectLastBootedGame() {
|
|||
}
|
||||
const QString lastfile = m_config->getMRU().first();
|
||||
if (m_entries.contains(lastfile)) {
|
||||
selectEntry(m_entries.value(lastfile));
|
||||
selectEntry(lastfile);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryController::loadDirectory(const QString& dir, bool recursive) {
|
||||
// This class can get deleted during this function (sigh) so we need to hold onto this
|
||||
std::shared_ptr<mLibrary> library = m_library;
|
||||
qint64 libraryJob = m_libraryJob;
|
||||
mLibraryLoadDirectory(library.get(), dir.toUtf8().constData(), recursive);
|
||||
}
|
||||
|
||||
void LibraryController::freeLibrary() {
|
||||
for (size_t i = 0; i < mLibraryListingSize(&m_listing); ++i) {
|
||||
mLibraryEntryFree(mLibraryListingGetPointer(&m_listing, i));
|
||||
}
|
||||
mLibraryListingClear(&m_listing);
|
||||
m_libraryJob.testAndSetOrdered(libraryJob, -1);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Copyright (c) 2014-2017 waddlesplash
|
||||
* Copyright (c) 2014-2020 Jeffrey Pfau
|
||||
* Copyright (c) 2013-2021 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -8,8 +8,9 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include <QAtomicInteger>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include <mgba/core/library.h>
|
||||
|
@ -28,40 +29,42 @@ enum class LibraryStyle {
|
|||
STYLE_ICON
|
||||
};
|
||||
|
||||
class LibraryEntry final {
|
||||
public:
|
||||
LibraryEntry(mLibraryEntry* entry);
|
||||
struct LibraryEntry {
|
||||
LibraryEntry() {}
|
||||
LibraryEntry(const mLibraryEntry* entry);
|
||||
|
||||
QString displayTitle() const { return title().isNull() ? filename() : title(); }
|
||||
bool isNull() const { return fullpath.isNull(); }
|
||||
|
||||
QString base() const { return QString(entry->base); }
|
||||
QString filename() const { return QString(entry->filename); }
|
||||
QString fullpath() const { return m_fullpath; }
|
||||
QString title() const { return QString(entry->title); }
|
||||
QByteArray internalTitle() const { return QByteArray(entry->internalTitle); }
|
||||
QByteArray internalCode() const { return QByteArray(entry->internalCode); }
|
||||
mPlatform platform() const { return entry->platform; }
|
||||
size_t filesize() const { return entry->filesize; }
|
||||
uint32_t crc32() const { return entry->crc32; }
|
||||
QString displayTitle() const { return title.isNull() ? filename : title; }
|
||||
|
||||
const mLibraryEntry* entry;
|
||||
private:
|
||||
const QString m_fullpath;
|
||||
QString base;
|
||||
QString filename;
|
||||
QString fullpath;
|
||||
QString title;
|
||||
QByteArray internalTitle;
|
||||
QByteArray internalCode;
|
||||
mPlatform platform;
|
||||
size_t filesize;
|
||||
uint32_t crc32;
|
||||
|
||||
bool operator==(const LibraryEntry& other) const { return other.fullpath == fullpath; }
|
||||
};
|
||||
typedef std::shared_ptr<LibraryEntry> LibraryEntryRef;
|
||||
|
||||
class AbstractGameList {
|
||||
public:
|
||||
virtual LibraryEntryRef selectedEntry() = 0;
|
||||
virtual void selectEntry(LibraryEntryRef game) = 0;
|
||||
virtual QString selectedEntry() = 0;
|
||||
virtual void selectEntry(const QString& fullpath) = 0;
|
||||
|
||||
virtual void setViewStyle(LibraryStyle newStyle) = 0;
|
||||
|
||||
virtual void addEntry(LibraryEntryRef item) = 0;
|
||||
virtual void addEntries(QList<LibraryEntryRef> items);
|
||||
virtual void resetEntries(const QList<LibraryEntry>&) = 0;
|
||||
virtual void addEntries(const QList<LibraryEntry>&) = 0;
|
||||
virtual void updateEntries(const QList<LibraryEntry>&) = 0;
|
||||
virtual void removeEntries(const QList<QString>&) = 0;
|
||||
|
||||
virtual void removeEntry(LibraryEntryRef item) = 0;
|
||||
virtual void removeEntries(QList<LibraryEntryRef> items);
|
||||
virtual void addEntry(const LibraryEntry&);
|
||||
virtual void updateEntry(const LibraryEntry&);
|
||||
virtual void removeEntry(const QString&);
|
||||
|
||||
virtual QWidget* widget() = 0;
|
||||
};
|
||||
|
@ -77,8 +80,8 @@ public:
|
|||
LibraryStyle viewStyle() const { return m_currentStyle; }
|
||||
void setViewStyle(LibraryStyle newStyle);
|
||||
|
||||
void selectEntry(LibraryEntryRef entry);
|
||||
LibraryEntryRef selectedEntry();
|
||||
void selectEntry(const QString& fullpath);
|
||||
LibraryEntry selectedEntry();
|
||||
VFile* selectedVFile();
|
||||
QPair<QString, QString> selectedPath();
|
||||
|
||||
|
@ -98,13 +101,11 @@ private slots:
|
|||
|
||||
private:
|
||||
void loadDirectory(const QString&, bool recursive = true); // Called on separate thread
|
||||
void freeLibrary();
|
||||
|
||||
ConfigController* m_config = nullptr;
|
||||
std::shared_ptr<mLibrary> m_library;
|
||||
qint64 m_libraryJob = -1;
|
||||
mLibraryListing m_listing;
|
||||
QMap<QString, LibraryEntryRef> m_entries;
|
||||
QAtomicInteger<qint64> m_libraryJob = -1;
|
||||
QHash<QString, LibraryEntry> m_entries;
|
||||
|
||||
LibraryStyle m_currentStyle;
|
||||
AbstractGameList* m_currentList = nullptr;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright (c) 2014-2017 waddlesplash
|
||||
* Copyright (c) 2013-2021 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -23,22 +24,16 @@ LibraryGrid::~LibraryGrid() {
|
|||
delete m_widget;
|
||||
}
|
||||
|
||||
LibraryEntryRef LibraryGrid::selectedEntry() {
|
||||
QString LibraryGrid::selectedEntry() {
|
||||
if (!m_widget->selectedItems().empty()) {
|
||||
return m_items.key(m_widget->selectedItems().at(0));
|
||||
} else {
|
||||
return LibraryEntryRef();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryGrid::selectEntry(LibraryEntryRef game) {
|
||||
if (!game) {
|
||||
return;
|
||||
}
|
||||
if (!m_widget->selectedItems().empty()) {
|
||||
m_widget->selectedItems().at(0)->setSelected(false);
|
||||
}
|
||||
m_items.value(game)->setSelected(true);
|
||||
void LibraryGrid::selectEntry(const QString& game) {
|
||||
m_widget->setCurrentItem(m_items.value(game));
|
||||
}
|
||||
|
||||
void LibraryGrid::setViewStyle(LibraryStyle newStyle) {
|
||||
|
@ -47,33 +42,62 @@ void LibraryGrid::setViewStyle(LibraryStyle newStyle) {
|
|||
m_widget->setIconSize(QSize(GRID_BANNER_WIDTH, GRID_BANNER_HEIGHT));
|
||||
m_widget->setViewMode(QListView::IconMode);
|
||||
} else {
|
||||
m_currentStyle = LibraryStyle::STYLE_ICON;
|
||||
m_widget->setIconSize(QSize(ICON_BANNER_WIDTH, ICON_BANNER_HEIGHT));
|
||||
m_widget->setViewMode(QListView::ListMode);
|
||||
}
|
||||
m_currentStyle = newStyle;
|
||||
|
||||
// QListView resets this when you change the view mode, so let's set it again
|
||||
m_widget->setDragEnabled(false);
|
||||
}
|
||||
|
||||
void LibraryGrid::addEntry(LibraryEntryRef item) {
|
||||
if (m_items.contains(item)) {
|
||||
void LibraryGrid::resetEntries(const QList<LibraryEntry>& items) {
|
||||
m_widget->clear();
|
||||
m_items.clear();
|
||||
addEntries(items);
|
||||
}
|
||||
|
||||
void LibraryGrid::addEntries(const QList<LibraryEntry>& items) {
|
||||
for (const auto& item : items) {
|
||||
addEntry(item);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryGrid::addEntry(const LibraryEntry& item) {
|
||||
if (m_items.contains(item.fullpath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* i = new QListWidgetItem;
|
||||
i->setText(item->displayTitle());
|
||||
i->setText(item.displayTitle());
|
||||
|
||||
m_widget->addItem(i);
|
||||
m_items.insert(item, i);
|
||||
m_items.insert(item.fullpath, i);
|
||||
}
|
||||
|
||||
void LibraryGrid::removeEntry(LibraryEntryRef entry) {
|
||||
if (!m_items.contains(entry)) {
|
||||
void LibraryGrid::updateEntries(const QList<LibraryEntry>& items) {
|
||||
for (const auto& item : items) {
|
||||
updateEntry(item);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryGrid::updateEntry(const LibraryEntry& item) {
|
||||
QListWidgetItem* i = m_items.value(item.fullpath);
|
||||
i->setText(item.displayTitle());
|
||||
}
|
||||
|
||||
void LibraryGrid::removeEntries(const QList<QString>& items) {
|
||||
for (const auto& item : items) {
|
||||
removeEntry(item);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryGrid::removeEntry(const QString& item) {
|
||||
if (!m_items.contains(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete m_items.take(entry);
|
||||
delete m_items.take(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,16 +16,21 @@ public:
|
|||
explicit LibraryGrid(LibraryController* parent = nullptr);
|
||||
~LibraryGrid();
|
||||
|
||||
// AbstractGameList stuff
|
||||
virtual LibraryEntryRef selectedEntry() override;
|
||||
virtual void selectEntry(LibraryEntryRef game) override;
|
||||
QString selectedEntry() override;
|
||||
void selectEntry(const QString& fullpath) override;
|
||||
|
||||
virtual void setViewStyle(LibraryStyle newStyle) override;
|
||||
void setViewStyle(LibraryStyle newStyle) override;
|
||||
|
||||
virtual void addEntry(LibraryEntryRef item) override;
|
||||
virtual void removeEntry(LibraryEntryRef entry) override;
|
||||
void resetEntries(const QList<LibraryEntry>& items) override;
|
||||
void addEntries(const QList<LibraryEntry>& items) override;
|
||||
void updateEntries(const QList<LibraryEntry>& items) override;
|
||||
void removeEntries(const QList<QString>& items) override;
|
||||
|
||||
virtual QWidget* widget() override { return m_widget; }
|
||||
void addEntry(const LibraryEntry& items) override;
|
||||
void updateEntry(const LibraryEntry& items) override;
|
||||
void removeEntry(const QString& items) override;
|
||||
|
||||
QWidget* widget() override { return m_widget; }
|
||||
|
||||
signals:
|
||||
void startGame();
|
||||
|
@ -40,7 +45,7 @@ private:
|
|||
const quint32 ICON_BANNER_WIDTH = 64;
|
||||
const quint32 ICON_BANNER_HEIGHT = 64;
|
||||
|
||||
QMap<LibraryEntryRef, QListWidgetItem*> m_items;
|
||||
QHash<QString, QListWidgetItem*> m_items;
|
||||
LibraryStyle m_currentStyle;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class TreeWidgetItem : public QTreeWidgetItem {
|
||||
class LibraryTreeItem : public QTreeWidgetItem {
|
||||
public:
|
||||
TreeWidgetItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {}
|
||||
LibraryTreeItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {}
|
||||
void setFilesize(size_t size);
|
||||
|
||||
virtual bool operator<(const QTreeWidgetItem& other) const override;
|
||||
|
@ -22,15 +24,17 @@ protected:
|
|||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
void TreeWidgetItem::setFilesize(size_t size) {
|
||||
}
|
||||
|
||||
void LibraryTreeItem::setFilesize(size_t size) {
|
||||
m_size = size;
|
||||
setText(LibraryTree::COL_SIZE, niceSizeFormat(size));
|
||||
}
|
||||
|
||||
bool TreeWidgetItem::operator<(const QTreeWidgetItem& other) const {
|
||||
bool LibraryTreeItem::operator<(const QTreeWidgetItem& other) const {
|
||||
const int column = treeWidget()->sortColumn();
|
||||
return ((column == LibraryTree::COL_SIZE) ?
|
||||
m_size < dynamic_cast<const TreeWidgetItem*>(&other)->m_size :
|
||||
m_size < dynamic_cast<const LibraryTreeItem*>(&other)->m_size :
|
||||
QTreeWidgetItem::operator<(other));
|
||||
}
|
||||
|
||||
|
@ -56,16 +60,14 @@ LibraryTree::LibraryTree(LibraryController* parent)
|
|||
m_widget->sortByColumn(COL_NAME, Qt::AscendingOrder);
|
||||
|
||||
QObject::connect(m_widget, &QTreeWidget::itemActivated, [this](QTreeWidgetItem* item, int) -> void {
|
||||
if (!m_pathNodes.values().contains(item)) {
|
||||
if (m_items.values().contains(item)) {
|
||||
emit m_controller->startGame();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LibraryTree::~LibraryTree() {
|
||||
for (QTreeWidgetItem* i : m_items.values()) {
|
||||
delete i;
|
||||
}
|
||||
m_widget->clear();
|
||||
}
|
||||
|
||||
void LibraryTree::resizeAllCols() {
|
||||
|
@ -74,75 +76,97 @@ void LibraryTree::resizeAllCols() {
|
|||
}
|
||||
}
|
||||
|
||||
LibraryEntryRef LibraryTree::selectedEntry() {
|
||||
QString LibraryTree::selectedEntry() {
|
||||
if (!m_widget->selectedItems().empty()) {
|
||||
return m_items.key(m_widget->selectedItems().at(0));
|
||||
} else {
|
||||
return LibraryEntryRef();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryTree::selectEntry(LibraryEntryRef game) {
|
||||
if (!game) {
|
||||
void LibraryTree::selectEntry(const QString& game) {
|
||||
if (game.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!m_widget->selectedItems().empty()) {
|
||||
m_widget->selectedItems().at(0)->setSelected(false);
|
||||
}
|
||||
m_items.value(game)->setSelected(true);
|
||||
m_widget->setCurrentItem(m_items.value(game));
|
||||
}
|
||||
|
||||
void LibraryTree::setViewStyle(LibraryStyle newStyle) {
|
||||
if (newStyle == LibraryStyle::STYLE_LIST) {
|
||||
m_currentStyle = LibraryStyle::STYLE_LIST;
|
||||
m_widget->setIndentation(0);
|
||||
rebuildTree();
|
||||
} else {
|
||||
m_currentStyle = LibraryStyle::STYLE_TREE;
|
||||
m_widget->setIndentation(20);
|
||||
rebuildTree();
|
||||
}
|
||||
m_currentStyle = newStyle;
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void LibraryTree::addEntries(QList<LibraryEntryRef> items) {
|
||||
void LibraryTree::resetEntries(const QList<LibraryEntry>& items) {
|
||||
m_deferredTreeRebuild = true;
|
||||
AbstractGameList::addEntries(items);
|
||||
m_entries.clear();
|
||||
m_pathNodes.clear();
|
||||
addEntries(items);
|
||||
}
|
||||
|
||||
void LibraryTree::addEntries(const QList<LibraryEntry>& items) {
|
||||
m_deferredTreeRebuild = true;
|
||||
for (const auto& item : items) {
|
||||
addEntry(item);
|
||||
}
|
||||
m_deferredTreeRebuild = false;
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void LibraryTree::addEntry(LibraryEntryRef item) {
|
||||
if (m_items.contains(item)) {
|
||||
return;
|
||||
}
|
||||
void LibraryTree::addEntry(const LibraryEntry& item) {
|
||||
m_entries[item.fullpath] = item;
|
||||
|
||||
QString folder = item->base();
|
||||
QString folder = item.base;
|
||||
if (!m_pathNodes.contains(folder)) {
|
||||
QTreeWidgetItem* i = new TreeWidgetItem;
|
||||
i->setText(0, folder.section("/", -1));
|
||||
m_pathNodes.insert(folder, i);
|
||||
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
|
||||
m_widget->addTopLevelItem(i);
|
||||
}
|
||||
m_pathNodes.insert(folder, 1);
|
||||
} else {
|
||||
++m_pathNodes[folder];
|
||||
}
|
||||
|
||||
TreeWidgetItem* i = new TreeWidgetItem;
|
||||
i->setText(COL_NAME, item->displayTitle());
|
||||
i->setText(COL_LOCATION, QDir::toNativeSeparators(item->base()));
|
||||
i->setText(COL_PLATFORM, nicePlatformFormat(item->platform()));
|
||||
i->setFilesize(item->filesize());
|
||||
i->setTextAlignment(COL_SIZE, Qt::AlignRight);
|
||||
i->setText(COL_CRC32, QString("%0").arg(item->crc32(), 8, 16, QChar('0')));
|
||||
m_items.insert(item, i);
|
||||
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void LibraryTree::removeEntry(LibraryEntryRef item) {
|
||||
if (!m_items.contains(item)) {
|
||||
void LibraryTree::updateEntries(const QList<LibraryEntry>& items) {
|
||||
for (const auto& item : items) {
|
||||
updateEntry(item);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryTree::updateEntry(const LibraryEntry& item) {
|
||||
m_entries[item.fullpath] = item;
|
||||
|
||||
LibraryTreeItem* i = static_cast<LibraryTreeItem*>(m_items.value(item.fullpath));
|
||||
i->setText(COL_NAME, item.displayTitle());
|
||||
i->setText(COL_PLATFORM, nicePlatformFormat(item.platform));
|
||||
i->setFilesize(item.filesize);
|
||||
i->setText(COL_CRC32, QString("%0").arg(item.crc32, 8, 16, QChar('0')));
|
||||
}
|
||||
|
||||
void LibraryTree::removeEntries(const QList<QString>& items) {
|
||||
m_deferredTreeRebuild = true;
|
||||
for (const auto& item : items) {
|
||||
removeEntry(item);
|
||||
}
|
||||
m_deferredTreeRebuild = false;
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void LibraryTree::removeEntry(const QString& item) {
|
||||
if (!m_entries.contains(item)) {
|
||||
return;
|
||||
}
|
||||
delete m_items.take(item);
|
||||
QString folder = m_entries.value(item).base;
|
||||
--m_pathNodes[folder];
|
||||
if (m_pathNodes[folder] <= 0) {
|
||||
m_pathNodes.remove(folder);
|
||||
}
|
||||
|
||||
m_entries.remove(item);
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void LibraryTree::rebuildTree() {
|
||||
|
@ -150,26 +174,33 @@ void LibraryTree::rebuildTree() {
|
|||
return;
|
||||
}
|
||||
|
||||
LibraryEntryRef currentGame = selectedEntry();
|
||||
|
||||
int count = m_widget->topLevelItemCount();
|
||||
for (int a = count - 1; a >= 0; --a) {
|
||||
m_widget->takeTopLevelItem(a);
|
||||
}
|
||||
|
||||
for (QTreeWidgetItem* i : m_pathNodes.values()) {
|
||||
i->takeChildren();
|
||||
}
|
||||
QString currentGame = selectedEntry();
|
||||
m_widget->clear();
|
||||
m_items.clear();
|
||||
|
||||
QHash<QString, QTreeWidgetItem*> pathNodes;
|
||||
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
|
||||
for (QTreeWidgetItem* i : m_pathNodes.values()) {
|
||||
for (const QString& folder : m_pathNodes.keys()) {
|
||||
QTreeWidgetItem* i = new LibraryTreeItem;
|
||||
pathNodes.insert(folder, i);
|
||||
i->setText(0, folder.section("/", -1));
|
||||
m_widget->addTopLevelItem(i);
|
||||
}
|
||||
for (QTreeWidgetItem* i : m_items.values()) {
|
||||
m_pathNodes.value(m_items.key(i)->base())->addChild(i);
|
||||
}
|
||||
} else {
|
||||
for (QTreeWidgetItem* i : m_items.values()) {
|
||||
}
|
||||
|
||||
for (const auto& item : m_entries.values()) {
|
||||
LibraryTreeItem* i = new LibraryTreeItem;
|
||||
i->setText(COL_NAME, item.displayTitle());
|
||||
i->setText(COL_LOCATION, QDir::toNativeSeparators(item.base));
|
||||
i->setText(COL_PLATFORM, nicePlatformFormat(item.platform));
|
||||
i->setFilesize(item.filesize);
|
||||
i->setTextAlignment(COL_SIZE, Qt::AlignRight);
|
||||
i->setText(COL_CRC32, QString("%0").arg(item.crc32, 8, 16, QChar('0')));
|
||||
m_items.insert(item.fullpath, i);
|
||||
|
||||
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
|
||||
pathNodes.value(item.base)->addChild(i);
|
||||
} else {
|
||||
m_widget->addTopLevelItem(i);
|
||||
}
|
||||
}
|
||||
|
@ -178,5 +209,3 @@ void LibraryTree::rebuildTree() {
|
|||
resizeAllCols();
|
||||
selectEntry(currentGame);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace QGBA {
|
||||
|
||||
class LibraryTreeItem;
|
||||
|
||||
class LibraryTree final : public AbstractGameList {
|
||||
|
||||
public:
|
||||
|
@ -25,17 +27,21 @@ public:
|
|||
explicit LibraryTree(LibraryController* parent = nullptr);
|
||||
~LibraryTree();
|
||||
|
||||
// AbstractGameList stuff
|
||||
virtual LibraryEntryRef selectedEntry() override;
|
||||
virtual void selectEntry(LibraryEntryRef game) override;
|
||||
QString selectedEntry() override;
|
||||
void selectEntry(const QString& fullpath) override;
|
||||
|
||||
virtual void setViewStyle(LibraryStyle newStyle) override;
|
||||
void setViewStyle(LibraryStyle newStyle) override;
|
||||
|
||||
virtual void addEntries(QList<LibraryEntryRef> items) override;
|
||||
virtual void addEntry(LibraryEntryRef item) override;
|
||||
virtual void removeEntry(LibraryEntryRef item) override;
|
||||
void resetEntries(const QList<LibraryEntry>& items) override;
|
||||
void addEntries(const QList<LibraryEntry>& items) override;
|
||||
void updateEntries(const QList<LibraryEntry>& items) override;
|
||||
void removeEntries(const QList<QString>& items) override;
|
||||
|
||||
virtual QWidget* widget() override { return m_widget; }
|
||||
void addEntry(const LibraryEntry& items) override;
|
||||
void updateEntry(const LibraryEntry& items) override;
|
||||
void removeEntry(const QString& items) override;
|
||||
|
||||
QWidget* widget() override { return m_widget; }
|
||||
|
||||
private:
|
||||
QTreeWidget* m_widget;
|
||||
|
@ -44,8 +50,9 @@ private:
|
|||
LibraryController* m_controller;
|
||||
|
||||
bool m_deferredTreeRebuild = false;
|
||||
QMap<LibraryEntryRef, QTreeWidgetItem*> m_items;
|
||||
QMap<QString, QTreeWidgetItem*> m_pathNodes;
|
||||
QHash<QString, LibraryEntry> m_entries;
|
||||
QHash<QString, QTreeWidgetItem*> m_items;
|
||||
QHash<QString, int> m_pathNodes;
|
||||
|
||||
void rebuildTree();
|
||||
void resizeAllCols();
|
||||
|
|
|
@ -124,7 +124,7 @@ static enum ScreenMode {
|
|||
SM_MAX
|
||||
} screenMode = SM_PA;
|
||||
|
||||
static bool initEgl() {
|
||||
static bool eglInit() {
|
||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!s_display) {
|
||||
goto _fail0;
|
||||
|
@ -173,7 +173,7 @@ _fail0:
|
|||
return false;
|
||||
}
|
||||
|
||||
static void deinitEgl() {
|
||||
static void eglDeinit() {
|
||||
if (s_display) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context) {
|
||||
|
@ -186,7 +186,7 @@ static void deinitEgl() {
|
|||
}
|
||||
}
|
||||
|
||||
static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
|
||||
static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, int key) {
|
||||
mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
|
||||
}
|
||||
|
||||
|
@ -674,29 +674,25 @@ static void _guiFinish(void) {
|
|||
GUIFontDrawSubmit(font);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
NWindow* window = nwindowGetDefault();
|
||||
nwindowSetDimensions(window, 1920, 1080);
|
||||
static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
|
||||
SwkbdConfig swkbd;
|
||||
swkbdCreate(&swkbd, 0);
|
||||
swkbdConfigMakePresetDefault(&swkbd);
|
||||
swkbdConfigSetStringLenMax(&swkbd, keyboard->maxLen);
|
||||
swkbdConfigSetInitialText(&swkbd, keyboard->result);
|
||||
swkbdConfigSetHeaderText(&swkbd, keyboard->title);
|
||||
swkbdConfigSetReturnButtonFlag(&swkbd, keyboard->multiline);
|
||||
|
||||
socketInitializeDefault();
|
||||
nxlinkStdio();
|
||||
initEgl();
|
||||
romfsInit();
|
||||
audoutInitialize();
|
||||
psmInitialize();
|
||||
|
||||
font = GUIFontCreate();
|
||||
|
||||
vmode = appletGetOperationMode();
|
||||
if (vmode == AppletOperationMode_Console) {
|
||||
vwidth = 1920;
|
||||
vheight = 1080;
|
||||
Result rc = swkbdShow(&swkbd, keyboard->result, sizeof(keyboard->result));
|
||||
swkbdClose(&swkbd);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
return GUI_KEYBOARD_DONE;
|
||||
} else {
|
||||
vwidth = 1280;
|
||||
vheight = 720;
|
||||
return GUI_KEYBOARD_CANCEL;
|
||||
}
|
||||
nwindowSetCrop(window, 0, 0, vwidth, vheight);
|
||||
}
|
||||
|
||||
static void glInit(void) {
|
||||
glViewport(0, 1080 - vheight, vwidth, vheight);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
|
@ -783,7 +779,23 @@ int main(int argc, char* argv[]) {
|
|||
glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glEnableVertexAttribArray(offsetLocation);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
static void glDeinit(void) {
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
|
||||
glDeleteFramebuffers(1, ©Fbo);
|
||||
glDeleteTextures(1, &tex);
|
||||
glDeleteTextures(1, &oldTex);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glDeleteProgram(program);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
}
|
||||
|
||||
static void hidSetup(void) {
|
||||
hidInitializeTouchScreen();
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
padInitializeDefault(&pad);
|
||||
|
@ -808,6 +820,40 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
lightSensor.d.readLuminance = _lightSensorRead;
|
||||
lightSensor.d.sample = _lightSensorSample;
|
||||
}
|
||||
|
||||
static void hidTeardown(void) {
|
||||
hidStopSixAxisSensor(sixaxisHandles[0]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[1]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[2]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[3]);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
NWindow* window = nwindowGetDefault();
|
||||
nwindowSetDimensions(window, 1920, 1080);
|
||||
|
||||
socketInitializeDefault();
|
||||
nxlinkStdio();
|
||||
eglInit();
|
||||
romfsInit();
|
||||
audoutInitialize();
|
||||
psmInitialize();
|
||||
|
||||
font = GUIFontCreate();
|
||||
|
||||
vmode = appletGetOperationMode();
|
||||
if (vmode == AppletOperationMode_Console) {
|
||||
vwidth = 1920;
|
||||
vheight = 1080;
|
||||
} else {
|
||||
vwidth = 1280;
|
||||
vheight = 720;
|
||||
}
|
||||
nwindowSetCrop(window, 0, 0, vwidth, vheight);
|
||||
|
||||
glInit();
|
||||
hidSetup();
|
||||
|
||||
stream.videoDimensionsChanged = NULL;
|
||||
stream.postVideoFrame = NULL;
|
||||
|
@ -837,6 +883,7 @@ int main(int argc, char* argv[]) {
|
|||
_pollInput, _pollCursor,
|
||||
_batteryState,
|
||||
_guiPrepare, _guiFinish,
|
||||
_keyboardRun,
|
||||
},
|
||||
.keySources = (struct GUIInputKeys[]) {
|
||||
{
|
||||
|
@ -1028,27 +1075,13 @@ int main(int argc, char* argv[]) {
|
|||
audoutStopAudioOut();
|
||||
GUIFontDestroy(font);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
|
||||
glDeleteFramebuffers(1, ©Fbo);
|
||||
glDeleteTextures(1, &tex);
|
||||
glDeleteTextures(1, &oldTex);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glDeleteProgram(program);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
|
||||
hidStopSixAxisSensor(sixaxisHandles[0]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[1]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[2]);
|
||||
hidStopSixAxisSensor(sixaxisHandles[3]);
|
||||
glDeinit();
|
||||
hidTeardown();
|
||||
|
||||
psmExit();
|
||||
audoutExit();
|
||||
romfsExit();
|
||||
deinitEgl();
|
||||
eglDeinit();
|
||||
socketExit();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -72,3 +72,10 @@ void GUIInvalidateKeys(struct GUIParams* params) {
|
|||
params->inputHistory[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GUIKeyboardParamsInit(struct GUIKeyboardParams* keyboard) {
|
||||
memset(keyboard->title, 0, sizeof(keyboard->title));
|
||||
memset(keyboard->result, 0, sizeof(keyboard->result));
|
||||
keyboard->maxLen = sizeof(keyboard->result);
|
||||
keyboard->multiline = false;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,34 @@
|
|||
|
||||
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
|
||||
|
||||
void _itemNext(struct GUIMenuItem* item, bool wrap) {
|
||||
if (item->state < item->nStates - 1) {
|
||||
unsigned oldState = item->state;
|
||||
do {
|
||||
++item->state;
|
||||
} while (!item->validStates[item->state] && item->state < item->nStates - 1);
|
||||
if (!item->validStates[item->state]) {
|
||||
item->state = oldState;
|
||||
}
|
||||
} else if (wrap) {
|
||||
item->state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void _itemPrev(struct GUIMenuItem* item, bool wrap) {
|
||||
if (item->state > 0) {
|
||||
unsigned oldState = item->state;
|
||||
do {
|
||||
--item->state;
|
||||
} while (!item->validStates[item->state] && item->state > 0);
|
||||
if (!item->validStates[item->state]) {
|
||||
item->state = oldState;
|
||||
}
|
||||
} else if (wrap) {
|
||||
item->state = item->nStates - 1;
|
||||
}
|
||||
}
|
||||
|
||||
enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
|
||||
size_t start = 0;
|
||||
size_t lineHeight = GUIFontHeight(params->font);
|
||||
|
@ -52,15 +80,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
if (newInput & (1 << GUI_INPUT_LEFT)) {
|
||||
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
|
||||
if (item->validStates) {
|
||||
if (item->state > 0) {
|
||||
unsigned oldState = item->state;
|
||||
do {
|
||||
--item->state;
|
||||
} while (!item->validStates[item->state] && item->state > 0);
|
||||
if (!item->validStates[item->state]) {
|
||||
item->state = oldState;
|
||||
}
|
||||
}
|
||||
_itemPrev(item, false);
|
||||
} else if (menu->index >= pageSize) {
|
||||
menu->index -= pageSize;
|
||||
} else {
|
||||
|
@ -70,15 +90,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
if (newInput & (1 << GUI_INPUT_RIGHT)) {
|
||||
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
|
||||
if (item->validStates) {
|
||||
if (item->state < item->nStates - 1) {
|
||||
unsigned oldState = item->state;
|
||||
do {
|
||||
++item->state;
|
||||
} while (!item->validStates[item->state] && item->state < item->nStates - 1);
|
||||
if (!item->validStates[item->state]) {
|
||||
item->state = oldState;
|
||||
}
|
||||
}
|
||||
_itemNext(item, false);
|
||||
} else if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) {
|
||||
menu->index += pageSize;
|
||||
} else {
|
||||
|
@ -125,6 +137,8 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
if (reason != GUI_MENU_EXIT_BACK) {
|
||||
return reason;
|
||||
}
|
||||
} else if ((*item)->validStates && !(*item)->data) {
|
||||
_itemNext(*item, true);
|
||||
} else {
|
||||
return GUI_MENU_EXIT_ACCEPT;
|
||||
}
|
||||
|
|
|
@ -180,6 +180,30 @@ size_t toUtf8(uint32_t unichar, char* buffer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t toUtf16(uint32_t unichar, uint16_t* buffer) {
|
||||
if (unichar < 0xD800) {
|
||||
buffer[0] = unichar;
|
||||
return 1;
|
||||
}
|
||||
if (unichar < 0xE000) {
|
||||
// Orphan surrogate, invalid
|
||||
return 0;
|
||||
}
|
||||
if (unichar < 0x10000) {
|
||||
buffer[0] = unichar;
|
||||
return 1;
|
||||
}
|
||||
if (unichar < 0x110000) {
|
||||
unichar -= 0x10000;
|
||||
buffer[0] = 0xD800 | (unichar >> 10);
|
||||
buffer[1] = 0xDC00 | (unichar & 0x3FF);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Invalid code point
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length) {
|
||||
uint32_t char1 = 0, char2 = 0;
|
||||
while (utf16Length > 0 && utf8Length > 0) {
|
||||
|
@ -548,4 +572,4 @@ bool wildcard(const char* search, const char* string) {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|