mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
cc429330c7
4
CHANGES
4
CHANGES
|
@ -32,6 +32,8 @@ Emulation fixes:
|
|||
- GB Video: Delay LYC STAT check (fixes mgba.io/i/1331)
|
||||
- GB Video: Fix window being enabled mid-scanline (fixes mgba.io/i/1328)
|
||||
- GB I/O: Filter IE top bits properly (fixes mgba.io/i/1329)
|
||||
- GBA Video: Fix scanline cache with scale factor change edge cases
|
||||
- GBA DMA: Fix DMA0-2 lengths (fixes mgba.io/i/1344)
|
||||
Other fixes:
|
||||
- Qt: More app metadata fixes
|
||||
- Qt: Fix load recent from archive (fixes mgba.io/i/1325)
|
||||
|
@ -45,6 +47,8 @@ Misc:
|
|||
- Qt: Add missing HEVC NVENC option (fixes mgba.io/i/1323)
|
||||
- LR35902: Support PC-relative opcode decoding
|
||||
- Qt: Improve camera initialization
|
||||
- Qt: Support switching webcams
|
||||
- Core: Add keysRead callback
|
||||
|
||||
0.7.1: (2019-02-24)
|
||||
Bugfixes:
|
||||
|
|
|
@ -86,6 +86,7 @@ struct mCoreCallbacks {
|
|||
void (*videoFrameEnded)(void* context);
|
||||
void (*coreCrashed)(void* context);
|
||||
void (*sleep)(void* context);
|
||||
void (*keysRead)(void* context);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
|
||||
|
|
|
@ -28,6 +28,8 @@ const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share
|
|||
const char* ERROR_OVERFLOW = "Arguments overflow";
|
||||
const char* ERROR_INVALID_ARGS = "Invalid arguments";
|
||||
|
||||
static struct ParseTree* _parseTree(const char** string);
|
||||
|
||||
#if !defined(NDEBUG) && !defined(_WIN32)
|
||||
static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
#endif
|
||||
|
@ -84,12 +86,12 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "listw", _listWatchpoints, "", "List watchpoints" },
|
||||
{ "n", _next, "", "Execute next instruction" },
|
||||
{ "next", _next, "", "Execute next instruction" },
|
||||
{ "p", _print, "I", "Print a value" },
|
||||
{ "p/t", _printBin, "I", "Print a value as binary" },
|
||||
{ "p/x", _printHex, "I", "Print a value as hexadecimal" },
|
||||
{ "print", _print, "I", "Print a value" },
|
||||
{ "print/t", _printBin, "I", "Print a value as binary" },
|
||||
{ "print/x", _printHex, "I", "Print a value as hexadecimal" },
|
||||
{ "p", _print, "S+", "Print a value" },
|
||||
{ "p/t", _printBin, "S+", "Print a value as binary" },
|
||||
{ "p/x", _printHex, "S+", "Print a value as hexadecimal" },
|
||||
{ "print", _print, "S+", "Print a value" },
|
||||
{ "print/t", _printBin, "S+", "Print a value as binary" },
|
||||
{ "print/x", _printHex, "S+", "Print a value as hexadecimal" },
|
||||
{ "q", _quit, "", "Quit the emulator" },
|
||||
{ "quit", _quit, "", "Quit the emulator" },
|
||||
{ "reset", _reset, "", "Reset the emulation" },
|
||||
|
@ -158,33 +160,72 @@ static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv
|
|||
debugger->system->disassemble(debugger->system, dv);
|
||||
}
|
||||
|
||||
static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector* dv, int32_t* intValue, int* segmentValue) {
|
||||
size_t args = 0;
|
||||
struct CLIDebugVector* accum;
|
||||
for (accum = dv; accum; accum = accum->next) {
|
||||
++args;
|
||||
}
|
||||
const char** arglist = malloc(sizeof(const char*) * (args + 1));
|
||||
args = 0;
|
||||
for (accum = dv; accum; accum = accum->next) {
|
||||
arglist[args] = accum->charValue;
|
||||
++args;
|
||||
}
|
||||
arglist[args] = NULL;
|
||||
struct ParseTree* tree = _parseTree(arglist);
|
||||
free(arglist);
|
||||
|
||||
if (!tree) {
|
||||
return false;
|
||||
}
|
||||
if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) {
|
||||
parseFree(tree);
|
||||
free(tree);
|
||||
return false;
|
||||
}
|
||||
parseFree(tree);
|
||||
free(tree);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
for (; dv; dv = dv->next) {
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue);
|
||||
continue;
|
||||
int32_t intValue = 0;
|
||||
int segmentValue = -1;
|
||||
if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) {
|
||||
debugger->backend->printf(debugger->backend, "Parse error\n");
|
||||
return;
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %u", dv->intValue);
|
||||
if (segmentValue >= 0) {
|
||||
debugger->backend->printf(debugger->backend, " $%02X:%04X\n", segmentValue, intValue);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, " %u\n", intValue);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
}
|
||||
|
||||
static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
for (; dv; dv = dv->next) {
|
||||
int32_t intValue = 0;
|
||||
int segmentValue = -1;
|
||||
if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) {
|
||||
debugger->backend->printf(debugger->backend, "Parse error\n");
|
||||
return;
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0b");
|
||||
int i = 32;
|
||||
while (i--) {
|
||||
debugger->backend->printf(debugger->backend, "%u", (dv->intValue >> i) & 1);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "%u", (intValue >> i) & 1);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
}
|
||||
|
||||
static void _printHex(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
for (; dv; dv = dv->next) {
|
||||
debugger->backend->printf(debugger->backend, " 0x%08X", dv->intValue);
|
||||
int32_t intValue = 0;
|
||||
int segmentValue = -1;
|
||||
if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) {
|
||||
debugger->backend->printf(debugger->backend, "Parse error\n");
|
||||
return;
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
debugger->backend->printf(debugger->backend, " 0x%08X\n", intValue);
|
||||
}
|
||||
|
||||
static void _printHelp(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -460,31 +501,31 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct ParseTree* _parseTree(const char* string) {
|
||||
static struct ParseTree* _parseTree(const char** string) {
|
||||
struct LexVector lv;
|
||||
bool error = false;
|
||||
LexVectorInit(&lv, 0);
|
||||
size_t length = strlen(string);
|
||||
size_t adjusted = lexExpression(&lv, string, length, NULL);
|
||||
struct ParseTree* tree = malloc(sizeof(*tree));
|
||||
if (!adjusted) {
|
||||
size_t i;
|
||||
for (i = 0; string[i]; ++i) {
|
||||
size_t length = strlen(string[i]);
|
||||
size_t adjusted = lexExpression(&lv, string[i], length, NULL);
|
||||
if (!adjusted || adjusted > length) {
|
||||
error = true;
|
||||
} else {
|
||||
parseLexedExpression(tree, &lv);
|
||||
|
||||
if (adjusted > length) {
|
||||
error = true;
|
||||
} else {
|
||||
length -= adjusted;
|
||||
string += adjusted;
|
||||
}
|
||||
}
|
||||
struct ParseTree* tree = NULL;
|
||||
if (!error) {
|
||||
tree = malloc(sizeof(*tree));
|
||||
parseLexedExpression(tree, &lv);
|
||||
}
|
||||
lexFree(&lv);
|
||||
LexVectorClear(&lv);
|
||||
LexVectorDeinit(&lv);
|
||||
if (error) {
|
||||
if (tree) {
|
||||
parseFree(tree);
|
||||
free(tree);
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
return tree;
|
||||
|
@ -502,7 +543,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
struct ParseTree* tree = _parseTree((const char*[]) { dv->next->charValue, NULL });
|
||||
if (tree) {
|
||||
breakpoint.condition = tree;
|
||||
} else {
|
||||
|
@ -528,7 +569,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
.type = type
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
struct ParseTree* tree = _parseTree((const char*[]) { dv->next->charValue, NULL });
|
||||
if (tree) {
|
||||
watchpoint.condition = tree;
|
||||
} else {
|
||||
|
@ -746,10 +787,11 @@ static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandS
|
|||
|
||||
if (commands[i].format[arg] == '+') {
|
||||
dvNext = _parseArg(debugger, args, adjusted, lastArg);
|
||||
--args;
|
||||
--arg;
|
||||
} else {
|
||||
nextArgMandatory = isupper(commands[i].format[arg]) || (commands[i].format[arg] == '*');
|
||||
dvNext = _parseArg(debugger, args, adjusted, commands[i].format[arg]);
|
||||
lastArg = commands[i].format[arg];
|
||||
}
|
||||
|
||||
args += adjusted;
|
||||
|
|
|
@ -21,7 +21,6 @@ enum LexState {
|
|||
LEX_EXPECT_HEX_FIRST,
|
||||
LEX_EXPECT_HEX,
|
||||
LEX_EXPECT_PREFIX,
|
||||
LEX_EXPECT_OPERATOR,
|
||||
LEX_EXPECT_OPERATOR2,
|
||||
};
|
||||
|
||||
|
@ -91,31 +90,6 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta
|
|||
}
|
||||
*state = LEX_ERROR;
|
||||
}
|
||||
if (*state == LEX_ROOT || *state == LEX_ERROR) {
|
||||
struct Token lvNext;
|
||||
lvNext.type = TOKEN_OPERATOR_TYPE;
|
||||
*state = LEX_ROOT;
|
||||
switch (operator) {
|
||||
case '-':
|
||||
lvNext.operatorValue = OP_NEGATE;
|
||||
break;
|
||||
case '~':
|
||||
lvNext.operatorValue = OP_FLIP;
|
||||
break;
|
||||
case '!':
|
||||
lvNext.operatorValue = OP_NOT;
|
||||
break;
|
||||
case '*':
|
||||
lvNext.operatorValue = OP_DEREFERENCE;
|
||||
break;
|
||||
default:
|
||||
lvNext.type = TOKEN_ERROR_TYPE;
|
||||
*state = LEX_ERROR;
|
||||
return;
|
||||
}
|
||||
*LexVectorAppend(lv) = lvNext;
|
||||
return;
|
||||
}
|
||||
struct Token lvNext;
|
||||
lvNext.type = TOKEN_OPERATOR_TYPE;
|
||||
switch (operator) {
|
||||
|
@ -155,6 +129,9 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta
|
|||
case '!':
|
||||
lvNext.operatorValue = OP_NOT;
|
||||
break;
|
||||
case '~':
|
||||
lvNext.operatorValue = OP_FLIP;
|
||||
break;
|
||||
default:
|
||||
*state = LEX_ERROR;
|
||||
return;
|
||||
|
@ -190,14 +167,14 @@ static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexS
|
|||
lvNext->uintValue = next;
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
*state = LEX_EXPECT_OPERATOR;
|
||||
*state = LEX_ROOT;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_UINT_TYPE;
|
||||
lvNext->uintValue = next;
|
||||
*state = LEX_EXPECT_OPERATOR;
|
||||
*state = LEX_ROOT;
|
||||
break;
|
||||
default:
|
||||
*state = LEX_ERROR;
|
||||
|
@ -264,21 +241,30 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co
|
|||
state = LEX_EXPECT_HEX_FIRST;
|
||||
next = 0;
|
||||
break;
|
||||
case '%':
|
||||
state = LEX_EXPECT_BINARY_FIRST;
|
||||
next = 0;
|
||||
break;
|
||||
case '(':
|
||||
state = LEX_ROOT;
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_OPEN_PAREN_TYPE;
|
||||
break;
|
||||
case '!':
|
||||
case '=':
|
||||
case '+':
|
||||
case '-':
|
||||
case '~':
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
case '&':
|
||||
case '|':
|
||||
case '^':
|
||||
case '<':
|
||||
case '>':
|
||||
case '!':
|
||||
case '~':
|
||||
_lexOperator(lv, token, &state);
|
||||
break;
|
||||
case ')':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
|
@ -305,6 +291,7 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co
|
|||
case '<':
|
||||
case '>':
|
||||
case '!':
|
||||
case '~':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_IDENTIFIER_TYPE;
|
||||
lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
|
@ -316,14 +303,14 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co
|
|||
lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
state = LEX_ROOT;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_IDENTIFIER_TYPE;
|
||||
lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1);
|
||||
state = LEX_EXPECT_OPERATOR;
|
||||
state = LEX_ROOT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -446,33 +433,6 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case LEX_EXPECT_OPERATOR:
|
||||
switch (token) {
|
||||
case '=':
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
case '&':
|
||||
case '|':
|
||||
case '^':
|
||||
case '<':
|
||||
case '>':
|
||||
case '!':
|
||||
_lexOperator(lv, token, &state);
|
||||
break;
|
||||
case ')':
|
||||
lvNext = LexVectorAppend(lv);
|
||||
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
default:
|
||||
state = LEX_ERROR;
|
||||
}
|
||||
break;
|
||||
case LEX_ERROR:
|
||||
// This shouldn't be reached
|
||||
break;
|
||||
|
@ -494,7 +454,6 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co
|
|||
lvNext->identifierValue = strndup(tokenStart, string - tokenStart);
|
||||
break;
|
||||
case LEX_ROOT:
|
||||
case LEX_EXPECT_OPERATOR:
|
||||
case LEX_EXPECT_OPERATOR2:
|
||||
break;
|
||||
case LEX_EXPECT_BINARY_FIRST:
|
||||
|
@ -568,9 +527,6 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz
|
|||
tree->rhs = _parseTreeCreate();
|
||||
tree->token.type = TOKEN_SEGMENT_TYPE;
|
||||
i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens);
|
||||
if (tree->token.type == TOKEN_ERROR_TYPE) {
|
||||
tree->token.type = TOKEN_ERROR_TYPE;
|
||||
}
|
||||
break;
|
||||
case TOKEN_OPEN_PAREN_TYPE:
|
||||
++*openParens;
|
||||
|
@ -583,6 +539,21 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz
|
|||
--*openParens;
|
||||
return i + 1;
|
||||
case TOKEN_OPERATOR_TYPE:
|
||||
if (tree->token.type == TOKEN_ERROR_TYPE) {
|
||||
switch (token->operatorValue) {
|
||||
case OP_SUBTRACT:
|
||||
token->operatorValue = OP_NEGATE;
|
||||
break;
|
||||
case OP_MULTIPLY:
|
||||
token->operatorValue = OP_DEREFERENCE;
|
||||
break;
|
||||
case OP_NOT:
|
||||
case OP_FLIP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
newPrecedence = _operatorPrecedence[token->operatorValue];
|
||||
if (newPrecedence < precedence) {
|
||||
newTree = _parseTreeCreate();
|
||||
|
|
|
@ -59,14 +59,6 @@ M_TEST_DEFINE(lexBinary) {
|
|||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSigilBinary) {
|
||||
LEX("%10");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexHex) {
|
||||
LEX("0x10");
|
||||
|
||||
|
@ -111,13 +103,6 @@ M_TEST_DEFINE(lexTruncatedBinary) {
|
|||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexTruncatedSigilBinary) {
|
||||
LEX("%");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexTruncatedSigilHex) {
|
||||
LEX("$");
|
||||
|
||||
|
@ -350,6 +335,38 @@ M_TEST_DEFINE(lexIdentifierGreaterOperator) {
|
|||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexNotOperator) {
|
||||
LEX("!1");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 2);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NOT);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexNotNotOperator) {
|
||||
LEX("!!1");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NOT);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexFlipOperator) {
|
||||
LEX("~1");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 2);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_FLIP);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexEqualsOperator) {
|
||||
LEX("1==");
|
||||
|
||||
|
@ -510,138 +527,6 @@ M_TEST_DEFINE(lexIdentifierShiftROperator) {
|
|||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_R);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexEqualsInvalidOperator) {
|
||||
LEX("1=|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierEqualsInvalidOperator) {
|
||||
LEX("x=|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexNotInvalidOperator) {
|
||||
LEX("1!|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierNotInvalidOperator) {
|
||||
LEX("x!|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexLessInvalidOperator) {
|
||||
LEX("1<|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierLessInvalidOperator) {
|
||||
LEX("x<|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexGreaterInvalidOperator) {
|
||||
LEX("1>|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierGreaterInvalidOperator) {
|
||||
LEX("x>|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexAndInvalidOperator) {
|
||||
LEX("1&|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierAndInvalidOperator) {
|
||||
LEX("x&|");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexOrInvalidOperator) {
|
||||
LEX("1|>");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexIdentifierOrInvalidOperator) {
|
||||
LEX("x|>");
|
||||
|
||||
assert_int_equal(LexVectorSize(lv), 3);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE);
|
||||
assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x");
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR);
|
||||
assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(lexSimpleExpression) {
|
||||
LEX("1+1");
|
||||
|
||||
|
@ -782,7 +667,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
|||
cmocka_unit_test(lexInt),
|
||||
cmocka_unit_test(lexDecimal),
|
||||
cmocka_unit_test(lexBinary),
|
||||
cmocka_unit_test(lexSigilBinary),
|
||||
cmocka_unit_test(lexHex),
|
||||
cmocka_unit_test(lexSigilHex),
|
||||
cmocka_unit_test(lexSigilSegmentHex),
|
||||
|
@ -792,7 +676,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
|||
cmocka_unit_test(lexTruncatedHex),
|
||||
cmocka_unit_test(lexTruncatedSigilHex),
|
||||
cmocka_unit_test(lexTruncatedBinary),
|
||||
cmocka_unit_test(lexTruncatedSigilBinary),
|
||||
cmocka_unit_test(lexIdentifier),
|
||||
cmocka_unit_test(lexAddOperator),
|
||||
cmocka_unit_test(lexIdentifierAddOperator),
|
||||
|
@ -814,6 +697,9 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
|||
cmocka_unit_test(lexIdentifierLessOperator),
|
||||
cmocka_unit_test(lexGreaterOperator),
|
||||
cmocka_unit_test(lexIdentifierGreaterOperator),
|
||||
cmocka_unit_test(lexNotOperator),
|
||||
cmocka_unit_test(lexNotNotOperator),
|
||||
cmocka_unit_test(lexFlipOperator),
|
||||
cmocka_unit_test(lexEqualsOperator),
|
||||
cmocka_unit_test(lexIdentifierEqualsOperator),
|
||||
cmocka_unit_test(lexNotEqualsOperator),
|
||||
|
@ -830,18 +716,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer,
|
|||
cmocka_unit_test(lexIdentifierShiftLOperator),
|
||||
cmocka_unit_test(lexShiftROperator),
|
||||
cmocka_unit_test(lexIdentifierShiftROperator),
|
||||
cmocka_unit_test(lexEqualsInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierEqualsInvalidOperator),
|
||||
cmocka_unit_test(lexNotInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierNotInvalidOperator),
|
||||
cmocka_unit_test(lexLessInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierLessInvalidOperator),
|
||||
cmocka_unit_test(lexGreaterInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierGreaterInvalidOperator),
|
||||
cmocka_unit_test(lexAndInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierAndInvalidOperator),
|
||||
cmocka_unit_test(lexOrInvalidOperator),
|
||||
cmocka_unit_test(lexIdentifierOrInvalidOperator),
|
||||
cmocka_unit_test(lexSimpleExpression),
|
||||
cmocka_unit_test(lexOpenParen),
|
||||
cmocka_unit_test(lexCloseParen),
|
||||
|
|
|
@ -56,6 +56,12 @@ M_TEST_DEFINE(parseLexError) {
|
|||
assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(parseError) {
|
||||
PARSE("1 2");
|
||||
|
||||
assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(parseSimpleExpression) {
|
||||
PARSE("1+2");
|
||||
|
||||
|
@ -108,11 +114,35 @@ M_TEST_DEFINE(parseParentheticalAddMultplyExpression) {
|
|||
assert_int_equal(tree->rhs->token.uintValue, 3);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(parseIsolatedOperator) {
|
||||
PARSE("+");
|
||||
|
||||
assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(tree->lhs->token.type, TOKEN_ERROR_TYPE);
|
||||
assert_int_equal(tree->rhs->token.type, TOKEN_ERROR_TYPE);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(parseUnaryChainedOperator) {
|
||||
PARSE("1+*2");
|
||||
|
||||
assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(tree->token.operatorValue, OP_ADD);
|
||||
assert_int_equal(tree->lhs->token.type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(tree->lhs->token.uintValue, 1);
|
||||
assert_int_equal(tree->rhs->token.type, TOKEN_OPERATOR_TYPE);
|
||||
assert_int_equal(tree->rhs->token.operatorValue, OP_DEREFERENCE);
|
||||
assert_int_equal(tree->rhs->rhs->token.type, TOKEN_UINT_TYPE);
|
||||
assert_int_equal(tree->rhs->rhs->token.uintValue, 2);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Parser,
|
||||
cmocka_unit_test(parseEmpty),
|
||||
cmocka_unit_test(parseInt),
|
||||
cmocka_unit_test(parseLexError),
|
||||
cmocka_unit_test(parseError),
|
||||
cmocka_unit_test(parseSimpleExpression),
|
||||
cmocka_unit_test(parseAddMultplyExpression),
|
||||
cmocka_unit_test(parseParentheticalExpression),
|
||||
cmocka_unit_test(parseParentheticalAddMultplyExpression))
|
||||
cmocka_unit_test(parseParentheticalAddMultplyExpression),
|
||||
cmocka_unit_test(parseIsolatedOperator),
|
||||
cmocka_unit_test(parseUnaryChainedOperator))
|
||||
|
|
|
@ -576,6 +576,15 @@ static uint8_t _readKeysFiltered(struct GB* gb) {
|
|||
uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||
switch (address) {
|
||||
case REG_JOYP:
|
||||
{
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
|
||||
if (callbacks->keysRead) {
|
||||
callbacks->keysRead(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _readKeysFiltered(gb);
|
||||
case REG_IE:
|
||||
return gb->memory.ie;
|
||||
|
|
15
src/gba/io.c
15
src/gba/io.c
|
@ -461,19 +461,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
break;
|
||||
|
||||
case REG_DMA0CNT_LO:
|
||||
GBADMAWriteCNT_LO(gba, 0, value);
|
||||
GBADMAWriteCNT_LO(gba, 0, value & 0x3FFF);
|
||||
break;
|
||||
case REG_DMA0CNT_HI:
|
||||
value = GBADMAWriteCNT_HI(gba, 0, value);
|
||||
break;
|
||||
case REG_DMA1CNT_LO:
|
||||
GBADMAWriteCNT_LO(gba, 1, value);
|
||||
GBADMAWriteCNT_LO(gba, 1, value & 0x3FFF);
|
||||
break;
|
||||
case REG_DMA1CNT_HI:
|
||||
value = GBADMAWriteCNT_HI(gba, 1, value);
|
||||
break;
|
||||
case REG_DMA2CNT_LO:
|
||||
GBADMAWriteCNT_LO(gba, 2, value);
|
||||
GBADMAWriteCNT_LO(gba, 2, value & 0x3FFF);
|
||||
break;
|
||||
case REG_DMA2CNT_HI:
|
||||
value = GBADMAWriteCNT_HI(gba, 2, value);
|
||||
|
@ -725,6 +725,15 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
break;
|
||||
|
||||
case REG_KEYINPUT:
|
||||
{
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
if (callbacks->keysRead) {
|
||||
callbacks->keysRead(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
|
||||
return 0x3FF ^ gba->rr->queryInput(gba->rr);
|
||||
} else {
|
||||
|
|
|
@ -567,6 +567,14 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
|
||||
if (softwareRenderer->cache[y].scale[0][0] != softwareRenderer->bg[2].sx ||
|
||||
softwareRenderer->cache[y].scale[0][1] != softwareRenderer->bg[2].sy ||
|
||||
softwareRenderer->cache[y].scale[1][0] != softwareRenderer->bg[3].sx ||
|
||||
softwareRenderer->cache[y].scale[1][1] != softwareRenderer->bg[3].sy) {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
softwareRenderer->cache[y].scale[0][0] = softwareRenderer->bg[2].sx;
|
||||
softwareRenderer->cache[y].scale[0][1] = softwareRenderer->bg[2].sy;
|
||||
softwareRenderer->cache[y].scale[1][0] = softwareRenderer->bg[3].sx;
|
||||
|
|
|
@ -180,12 +180,22 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
m_ui.cameraDriver->addItem(tr("None (Still Image)"), static_cast<int>(InputController::CameraDriver::NONE));
|
||||
if (cameraDriver.isNull() || cameraDriver.toInt() == static_cast<int>(InputController::CameraDriver::NONE)) {
|
||||
m_ui.cameraDriver->setCurrentIndex(m_ui.cameraDriver->count() - 1);
|
||||
m_ui.camera->setEnabled(false);
|
||||
}
|
||||
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
m_ui.cameraDriver->addItem(tr("Qt Multimedia"), static_cast<int>(InputController::CameraDriver::QT_MULTIMEDIA));
|
||||
if (!cameraDriver.isNull() && cameraDriver.toInt() == static_cast<int>(InputController::CameraDriver::QT_MULTIMEDIA)) {
|
||||
m_ui.cameraDriver->setCurrentIndex(m_ui.cameraDriver->count() - 1);
|
||||
m_ui.camera->setEnabled(true);
|
||||
}
|
||||
QList<QPair<QByteArray, QString>> cameras = inputController->listCameras();
|
||||
QByteArray currentCamera = m_controller->getQtOption("camera").toByteArray();
|
||||
for (const auto& camera : cameras) {
|
||||
m_ui.camera->addItem(camera.second, camera.first);
|
||||
if (camera.first == currentCamera) {
|
||||
m_ui.camera->setCurrentIndex(m_ui.camera->count() - 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -447,6 +457,12 @@ void SettingsView::updateConfig() {
|
|||
emit cameraDriverChanged();
|
||||
}
|
||||
|
||||
QVariant camera = m_ui.camera->itemData(m_ui.camera->currentIndex());
|
||||
if (camera != m_controller->getQtOption("camera")) {
|
||||
m_controller->setQtOption("camera", camera);
|
||||
emit cameraChanged(camera.toByteArray());
|
||||
}
|
||||
|
||||
QLocale language = m_ui.languages->itemData(m_ui.languages->currentIndex()).toLocale();
|
||||
if (language != m_controller->getQtOption("language").toLocale() && !(language.bcp47Name() == QLocale::system().bcp47Name() && m_controller->getQtOption("language").isNull())) {
|
||||
m_controller->setQtOption("language", language.bcp47Name());
|
||||
|
|
|
@ -40,6 +40,7 @@ signals:
|
|||
void audioDriverChanged();
|
||||
void displayDriverChanged();
|
||||
void cameraDriverChanged();
|
||||
void cameraChanged(const QByteArray&);
|
||||
void pathsChanged();
|
||||
void languageChanged();
|
||||
void libraryCleared();
|
||||
|
|
|
@ -1341,7 +1341,7 @@
|
|||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_29">
|
||||
<property name="text">
|
||||
<string>Game Boy model</string>
|
||||
<string>Game Boy model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1377,7 +1377,7 @@
|
|||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_32">
|
||||
<property name="text">
|
||||
<string>Super Game Boy model</string>
|
||||
<string>Super Game Boy model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1413,7 +1413,7 @@
|
|||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_33">
|
||||
<property name="text">
|
||||
<string>Game Boy Color model</string>
|
||||
<string>Game Boy Color model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1752,6 +1752,26 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_35">
|
||||
<property name="text">
|
||||
<string>Camera:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QComboBox" name="camera">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
|
|
@ -489,6 +489,7 @@ void Window::openSettingsWindow() {
|
|||
connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::reloadDisplayDriver);
|
||||
connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver);
|
||||
connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart);
|
||||
connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera);
|
||||
connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart);
|
||||
connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig);
|
||||
#ifdef USE_SQLITE3
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
#include <QCamera>
|
||||
#include <QCameraInfo>
|
||||
#include <QVideoSurfaceFormat>
|
||||
#endif
|
||||
|
||||
|
@ -188,6 +188,10 @@ void InputController::setPlatform(mPlatform platform) {
|
|||
}
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
|
||||
QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray();
|
||||
if (!camera.isNull()) {
|
||||
QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera));
|
||||
}
|
||||
QMetaObject::invokeMethod(image->p, "setupCam");
|
||||
}
|
||||
#endif
|
||||
|
@ -960,6 +964,17 @@ void InputController::setCamImage(const QImage& image) {
|
|||
m_image.outOfDate = true;
|
||||
}
|
||||
|
||||
QList<QPair<QByteArray, QString>> InputController::listCameras() const {
|
||||
QList<QPair<QByteArray, QString>> out;
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
QList<QCameraInfo> cams = QCameraInfo::availableCameras();
|
||||
for (const auto& cam : cams) {
|
||||
out.append(qMakePair(cam.deviceName().toLatin1(), cam.description()));
|
||||
}
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
void InputController::increaseLuminanceLevel() {
|
||||
setLuminanceLevel(m_luxLevel + 1);
|
||||
}
|
||||
|
@ -994,7 +1009,7 @@ void InputController::setupCam() {
|
|||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
if (!m_camera) {
|
||||
m_camera = std::make_unique<QCamera>();
|
||||
connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings);
|
||||
connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
|
||||
}
|
||||
m_camera->setCaptureMode(QCamera::CaptureVideo);
|
||||
m_camera->setViewfinder(&m_videoDumper);
|
||||
|
@ -1052,3 +1067,17 @@ void InputController::teardownCam() {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputController::setCamera(const QByteArray& name) {
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
bool needsRestart = false;
|
||||
if (m_camera) {
|
||||
needsRestart = m_camera->state() == QCamera::ActiveState;
|
||||
}
|
||||
m_camera = std::make_unique<QCamera>(name);
|
||||
connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection);
|
||||
if (needsRestart) {
|
||||
setupCam();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ public:
|
|||
void stealFocus(QWidget* focus);
|
||||
void releaseFocus(QWidget* focus);
|
||||
|
||||
QList<QPair<QByteArray, QString>> listCameras() const;
|
||||
|
||||
mRumble* rumble();
|
||||
mRotationSource* rotationSource();
|
||||
mImageSource* imageSource() { return &m_image; }
|
||||
|
@ -141,6 +143,8 @@ public slots:
|
|||
void loadCamImage(const QString& path);
|
||||
void setCamImage(const QImage& image);
|
||||
|
||||
void setCamera(const QByteArray& id);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject*, QEvent*) override;
|
||||
|
||||
|
|
Loading…
Reference in New Issue