diff --git a/README.md b/README.md index 96e64acfb..29d9a0bc1 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1591. +This is the source code for early-access 1592. ## Legal Notice diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index 836dd25ca..3bc92b69d 100755 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -281,3 +281,380 @@ QWidget#controllerPlayer7, QWidget#controllerPlayer8 { background: transparent; } + +QDialog#QtSoftwareKeyboardDialog, +QStackedWidget#topOSK { + background: rgba(51, 51, 51, .9); +} + + +QDialog#OverlayDialog, +QStackedWidget#stackedDialog { + background: rgba(51, 51, 51, .7); +} + +QWidget#boxOSK, +QWidget#lineOSK, +QWidget#richDialog, +QWidget#lineDialog { + background: transparent; +} + +QStackedWidget#bottomOSK, +QWidget#contentDialog, +QWidget#contentRichDialog { + background: rgba(240, 240, 240, 1); +} + +QWidget#contentDialog, +QWidget#contentRichDialog { + margin: 5px; + border-radius: 6px; +} + +QWidget#buttonsDialog, +QWidget#buttonsRichDialog { + margin: 5px; + border-top: 2px solid rgba(44, 44, 44, 1); +} + +QWidget#legendOSKnum { + border-top: 1px solid rgba(44, 44, 44, 1); +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::vertical { + background: #cdcdcd; + width: 15px; + margin: 15px 3px 15px 3px; + border: 1px transparent; + border-radius: 4px; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::horizoncal { + background: #cdcdcd; + height: 15px; + margin: 3px 15px 3px 15px; + border: 1px transparent; + border-radius: 4px; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::handle { + background: #fff; + border-radius: 4px; + min-height: 5px; + min-width: 5px; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line, +QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line, +QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-page, +QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-page { + background: none; +} + +QWidget#inputOSK { + border-bottom: 3px solid rgba(255, 255, 255, .9); +} + +QWidget#inputOSK QLineEdit { + background: transparent; + border: none; + color: #ccc; +} + +QWidget#inputBoxOSK { + border: 2px solid rgba(255, 255, 255, .9); +} + +QWidget#inputBoxOSK QTextEdit { + background: transparent; + border: none; + color: #ccc; +} + +QWidget#richDialog QTextBrowser { + background: transparent; + border: none; + padding: 35px 65px; +} + + +QWidget#lineOSK QLabel#label_header { + color: #f0f0f0; +} + +QWidget#lineOSK QLabel#label_sub, +QWidget#lineOSK QLabel#label_characters, +QWidget#boxOSK QLabel#label_characters_box { + color: #ccc; +} + +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich { + color: #888; +} + +QWidget#contentDialog QLabel#label_dialog { + padding: 20px 65px; +} + +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich { + padding: 0px 65px; +} + +QDialog#OverlayDialog QPushButton { + color: rgba(49, 79, 239, 1); + background: transparent; + border: none; + padding: 0px; + min-width: 0px; +} + +QDialog#OverlayDialog QPushButton:focus, +QDialog#OverlayDialog QPushButton:hover { + color: rgba(49, 79, 239, 1); + background: rgba(255, 255, 255, 1); + border: 5px solid rgba(148, 250, 202, 1); + border-radius: 6px; + outline: none; +} + +QDialog#OverlayDialog QPushButton:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(150, 150, 150, 1); + border: 5px solid rgba(148, 250, 202, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton { + background: rgba(232, 232, 232, 1); + border: 2px solid rgba(240, 240, 240, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background: rgba(218, 218, 218, 1); + border: 2px solid rgba(240, 240, 240, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + color: rgba(240, 240, 240, 1); + background: rgba(44, 44, 44, 1); + border: 2px solid rgba(240, 240, 240, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + color: rgba(240, 240, 240, 1); + background: rgba(49, 79, 239, 1); + border: 2px solid rgba(240, 240, 240, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus, + +QDialog#QtSoftwareKeyboardDialog QPushButton:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover { + color: rgba(0, 0, 0, 1); + background: rgba(255, 255, 255, 1); + border: 5px solid rgba(148, 250, 202, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(150, 150, 150, 1); + border: 5px solid rgba(148, 250, 202, 1); + border-radius: 6px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_B.png); + qproperty-icon: url(:/overlay/osk_button_backspace.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_Y.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_plus.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift_on.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis { + padding-bottom: 7px; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel { + background: transparent; + color: #ccc; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_L, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num { + image: url(:/overlay/button_L.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num { + image: url(:/overlay/arrow_left.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_R, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num { + image: url(:/overlay/button_R.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num { + image: url(:/overlay/arrow_right.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick, +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift { + image: url(:/overlay/button_press_stick.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_X, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num { + image: url(:/overlay/button_X.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_A, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num { + image: url(:/overlay/button_A.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + color: rgba(164, 164, 164, 1); + background-color: rgba(218, 218, 218, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled { + color: rgba(164, 164, 164, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled { + background-image: url(:/overlay/osk_button_plus_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + background-image: url(:/overlay/osk_button_B_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled { + background-image: url(:/overlay/osk_button_Y_disabled.png); +} diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 2a1e8ddeb..8ce6d75f7 100755 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1560,7 +1560,400 @@ QWidget#controllerPlayer8 { background: transparent; } -/* touchscreen mapping widget */ -TouchScreenPreview { - qproperty-dotHighlightColor: #3daee9; +QDialog#QtSoftwareKeyboardDialog, +QStackedWidget#topOSK { + background: rgba(41, 41, 41, .9); +} + + +QDialog#OverlayDialog, +QStackedWidget#stackedDialog { + background: rgba(41, 41, 41, .7); +} + +QWidget#boxOSK, +QWidget#lineOSK, +QWidget#richDialog, +QWidget#lineDialog { + background: transparent; +} + +QStackedWidget#bottomOSK, +QWidget#contentDialog, +QWidget#contentRichDialog { + background: rgba(71, 69, 71, 1); +} + +QWidget#contentDialog, +QWidget#contentRichDialog { + margin: 5px; + border-radius: 6px; +} + +QWidget#buttonsDialog, +QWidget#buttonsRichDialog { + margin: 5px; + border-top: 2px solid rgba(255, 255, 255, .9); +} + +QWidget#legendOSKnum { + border-top: 1px solid rgba(255, 255, 255, 1); +} + +QStackedWidget#stackedDialog QTextBrowser QWidget { + background: transparent; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar { + background: #2a2929; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line, +QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line { + border-image: none; +} + +QWidget#inputOSK { + border-bottom: 3px solid rgba(255, 255, 255, .9); +} + +QWidget#inputOSK QLineEdit { + background: transparent; + border: none; + color: #ccc; + padding: 0px; +} + +QWidget#inputBoxOSK { + border: 2px solid rgba(255, 255, 255, .9); +} + +QWidget#inputBoxOSK QTextEdit { + background: transparent; + border: none; + color: #ccc; +} + +QWidget#richDialog QTextBrowser { + background: transparent; + border: none; + color: #fff; + padding: 35px 65px; +} + +QWidget#lineOSK QLabel#label_header { + color: #f0f0f0; +} + +QWidget#lineOSK QLabel#label_sub, +QWidget#lineOSK QLabel#label_characters, +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich, +QWidget#boxOSK QLabel#label_characters_box { + color: #ccc; +} + +QWidget#buttonsDialog, +QWidget#buttonsRichDialog, +QWidget#mainOSK, +QWidget#headerOSK, +QWidget#normalOSK, +QWidget#shiftOSK, +QWidget#numOSK, +QWidget#subOSK, +QWidget#inputOSK, +QWidget#inputBoxOSK, +QWidget#charactersOSK, +QWidget#charactersBoxOSK, +QWidget#legendOSK, +QWidget#legendOSK QWidget, +QWidget#legendOSKshift, +QWidget#legendOSKshift QWidget, +QWidget#legendOSKnum, +QWidget#legendOSKnum QWidget { + background: transparent; +} + +QWidget#contentDialog QLabel, +QWidget#legendOSK QLabel, +QWidget#legendOSKshift QLabel, +QWidget#legendOSKnum QLabel { + color: rgba(255, 255, 255, 1); +} + +QWidget#contentDialog QLabel#label_dialog { + padding: 20px 65px; +} + +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich { + padding: 0px 65px; +} + +QDialog#OverlayDialog QPushButton { + color: rgba(1, 253, 201, 1); + background: transparent; + border: none; + padding: 0px; + min-width: 0px; +} + +QDialog#OverlayDialog QPushButton:focus, +QDialog#OverlayDialog QPushButton:hover { + color: rgba(1, 253, 201, 1); + background: rgba(58, 61, 66, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#OverlayDialog QPushButton:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(150, 150, 150, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton { + color: rgba(255, 255, 255, 1); + background: rgba(80, 79, 80, 1); + border: 2px solid rgba(71, 69, 71, 1); + padding: 0px; + min-width: 0px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background: rgba(95, 94, 95, 1); + border: 2px solid rgba(71, 69, 71, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + color: rgba(240, 240, 240, 1); + background: rgba(255, 255, 255, 1); + border: 2px solid rgba(71, 69, 71, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + color: rgba(0, 0, 0, 1); + background: rgba(1, 253, 201, 1); + border: 2px solid rgba(71, 69, 71, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus, + +QDialog#QtSoftwareKeyboardDialog QPushButton:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover { + color: rgba(255, 255, 255, 1); + background: rgba(58, 61, 66, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(150, 150, 150, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_B_dark.png); + qproperty-icon: url(:/overlay/osk_button_backspace_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_Y_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + color: rgba(44, 44, 44, 1); + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_plus_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift_on_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis { + padding-bottom: 7px; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel { + background: transparent; + color: #ccc; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_L, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num { + image: url(:/overlay/button_L_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num { + image: url(:/overlay/arrow_left_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_R, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num { + image: url(:/overlay/button_R_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num { + image: url(:/overlay/arrow_right_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick, +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift { + image: url(:/overlay/button_press_stick_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_X, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num { + image: url(:/overlay/button_X_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_A, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num { + image: url(:/overlay/button_A_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + color: rgba(144, 144, 144, 1); + background-color: rgba(95, 94, 95, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled { + color: rgba(144, 144, 144, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled { + background-image: url(:/overlay/osk_button_plus_dark_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + background-image: url(:/overlay/osk_button_B_dark_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled { + background-image: url(:/overlay/osk_button_Y_dark_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QFrame, +QDialog#QtSoftwareKeyboardDialog QFrame[frameShape="0"], +QDialog#OverlayDialog QFrame, +QDialog#OverlayDialog QFrame[frameShape="0"] { + border-radius: 0px; + border: none; } diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index a64037455..64e1ecbcc 100755 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -1,10 +1,10 @@ /* --------------------------------------------------------------------------- - Created by the qtsass compiler v0.1.1 + Created by the qtsass compiler v0.1.1 - The definitions are in the "qdarkstyle.qss._styles.scss" module + The definitions are in the "qdarkstyle.qss._styles.scss" module - WARNING! All changes made in this file will be lost! + WARNING! All changes made in this file will be lost! --------------------------------------------------------------------------- */ /* QDarkStyleSheet ----------------------------------------------------------- @@ -15,34 +15,34 @@ It is based on three selecting colors, three greyish (background) colors plus three whitish (foreground) colors. Each set of widgets of the same type have a header like this: - ------------------ - GroupName -------- - ------------------ + ------------------ + GroupName -------- + ------------------ And each widget is separated with a header like this: - QWidgetName ------ + QWidgetName ------ This makes more easy to find and change some css field. The basic configuration is described bellow. - BACKGROUND ----------- + BACKGROUND ----------- - Light (unpressed) - Normal (border, disabled, pressed, checked, toolbars, menus) - Dark (background) + Light (unpressed) + Normal (border, disabled, pressed, checked, toolbars, menus) + Dark (background) - FOREGROUND ----------- + FOREGROUND ----------- - Light (texts/labels) - Normal (not used yet) - Dark (disabled texts) + Light (texts/labels) + Normal (not used yet) + Dark (disabled texts) - SELECTION ------------ + SELECTION ------------ - Light (selection/hover/active) - Normal (selected) - Dark (selected disabled) + Light (selection/hover/active) + Normal (selected) + Dark (selected disabled) If a stranger configuration is required because of a bugfix or anything else, keep the comment on the line above so nobody changes it, including the @@ -2483,3 +2483,404 @@ QWidget#controllerPlayer7, QWidget#controllerPlayer8 { background: transparent; } + +QDialog#QtSoftwareKeyboardDialog, +QStackedWidget#topOSK { + background: rgba(15, 25, 34, .9); +} + +QDialog#OverlayDialog, +QStackedWidget#stackedDialog { + background: rgba(15, 25, 34, .7); +} + +QWidget#boxOSK, +QWidget#lineOSK, +QWidget#richDialog, +QWidget#lineDialog { + background: transparent; +} + +QStackedWidget#bottomOSK, +QWidget#contentDialog, +QWidget#contentRichDialog { + background: rgba(31, 41, 51, 1); +} + +QWidget#contentDialog, +QWidget#contentRichDialog { + margin: 5px; + border-radius: 6px; +} + +QWidget#buttonsDialog, +QWidget#buttonsRichDialog { + margin: 5px; + border-top: 2px solid rgba(255, 255, 255, .9); +} + +QWidget#legendOSKnum { + border-top: 1px solid rgba(255, 255, 255, 1); +} + +QStackedWidget#stackedDialog QTextBrowser QWidget { + background: transparent; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar { + background: #19232d; + border: none; +} + +QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line, +QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line { + border-image: none; +} + +QWidget#mainOSK QStackedWidget, +QDialog#OverlayDialog QStackedWidget { + border: none; + padding: 0px; +} + +QWidget#inputOSK { + border-bottom: 3px solid rgba(255, 255, 255, .9); +} + +QWidget#inputOSK QLineEdit { + background: transparent; + border: none; + color: #ccc; + padding: 0px; +} + +QWidget#inputBoxOSK { + border: 2px solid rgba(255, 255, 255, .9); +} + +QWidget#inputBoxOSK QTextEdit { + background: transparent; + border: none; + color: #ccc; +} + +QWidget#richDialog QTextBrowser { + background: transparent; + border: none; + color: #fff; + padding: 35px 65px; +} + +QWidget#lineOSK QLabel#label_header { + color: #f0f0f0; +} + +QWidget#lineOSK QLabel#label_sub, +QWidget#lineOSK QLabel#label_characters, +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich, +QWidget#boxOSK QLabel#label_characters_box { + color: #ccc; +} + +QWidget#buttonsDialog, +QWidget#buttonsRichDialog, +QWidget#mainOSK, +QWidget#headerOSK, +QWidget#normalOSK, +QWidget#shiftOSK, +QWidget#numOSK, +QWidget#subOSK, +QWidget#inputOSK, +QWidget#inputBoxOSK, +QWidget#charactersOSK, +QWidget#charactersBoxOSK, +QWidget#legendOSK, +QWidget#legendOSK QWidget, +QWidget#legendOSKshift, +QWidget#legendOSKshift QWidget, +QWidget#legendOSKnum, +QWidget#legendOSKnum QWidget { + background: transparent; +} + +QWidget#contentDialog QLabel, +QWidget#legendOSK QLabel, +QWidget#legendOSKshift QLabel, +QWidget#legendOSKnum QLabel { + color: rgba(255, 255, 255, 1); +} + +QWidget#contentDialog QLabel#label_dialog { + padding: 20px 65px; +} + +QWidget#contentDialog QLabel#label_title, +QWidget#contentRichDialog QLabel#label_title_rich { + padding: 0px 65px; +} + +QDialog#OverlayDialog QPushButton { + color: rgba(1, 253, 201, 1); + background: transparent; + border: none; + padding: 0px; + min-width: 0px; +} + +QDialog#OverlayDialog QPushButton:focus, +QDialog#OverlayDialog QPushButton:hover { + color: rgba(1, 253, 201, 1); + background: rgba(18, 33, 46, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#OverlayDialog QPushButton:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(110, 122, 130, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QLabel { + padding: 0px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton { + color: rgba(255, 255, 255, 1); + background: rgba(40, 51, 60, 1); + border: 2px solid rgba(31, 41, 51, 1); + border-radius: 0px; + padding: 0px; + min-width: 0px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background: rgba(55, 66, 75, 1); + border: 2px solid rgba(31, 41, 51, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + color: rgba(240, 240, 240, 1); + background: rgba(255, 255, 255, 1); + border: 2px solid rgba(31, 41, 51, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + color: rgba(0, 0, 0, 1); + background: rgba(1, 253, 201, 1); + border: 2px solid rgba(31, 41, 51, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus, + +QDialog#QtSoftwareKeyboardDialog QPushButton:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover { + color: rgba(255, 255, 255, 1); + background: rgba(18, 33, 46, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; + outline: none; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed { + color: rgba(240, 240, 240, 1); + background: rgba(110, 122, 130, 1); + border: 5px solid rgba(56, 189, 225, 1); + border-radius: 6px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_B_dark.png); + qproperty-icon: url(:/overlay/osk_button_backspace_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift { + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_Y_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num { + color: rgba(44, 44, 44, 1); + background-position: right top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_plus_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift { + background-position: left top; + background-repeat: no-repeat; + background-origin: content; + background-image: url(:/overlay/osk_button_shift_lock_off.png); + qproperty-icon: url(:/overlay/osk_button_shift_on_dark.png); + qproperty-iconSize: 36px; +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis { + padding-bottom: 7px; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel { + background: transparent; + color: #ccc; +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_L, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num { + image: url(:/overlay/button_L_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num { + image: url(:/overlay/arrow_left_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_R, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num { + image: url(:/overlay/button_R_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num { + image: url(:/overlay/arrow_right_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick, +QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift { + image: url(:/overlay/button_press_stick_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_X, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num { + image: url(:/overlay/button_X_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QWidget#button_A, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift, +QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num { + image: url(:/overlay/button_A_dark.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + color: rgba(144, 144, 144, 1); + background-color: rgba(55, 66, 75, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled { + color: rgba(144, 144, 144, 1); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled { + background-image: url(:/overlay/osk_button_plus_dark_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled { + background-image: url(:/overlay/osk_button_B_dark_disabled.png); +} + +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled, +QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled { + background-image: url(:/overlay/osk_button_Y_dark_disabled.png); +} diff --git a/dist/yuzu.ico b/dist/yuzu.ico index df3be8464..7c998a5c5 100755 Binary files a/dist/yuzu.ico and b/dist/yuzu.ico differ diff --git a/dist/yuzu.svg b/dist/yuzu.svg index 93171d1bf..98ded2d8f 100755 --- a/dist/yuzu.svg +++ b/dist/yuzu.svg @@ -1 +1 @@ -Artboard 1 \ No newline at end of file +newAsset 7 \ No newline at end of file diff --git a/src/common/assert.cpp b/src/common/assert.cpp index d7d91b96b..c3b70797d 100755 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -4,8 +4,4 @@ #include "common/assert.h" -#include "common/common_funcs.h" - -void assert_handle_failure() { - Crash(); -} +void assert_handle_failure() {} diff --git a/src/common/nvidia_flags.h b/src/common/nvidia_flags.h index 75a0233ac..8930efcec 100755 --- a/src/common/nvidia_flags.h +++ b/src/common/nvidia_flags.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + namespace Common { /// Configure platform specific flags for Nvidia's driver diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 167ee13f3..7d9eba928 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -273,6 +273,7 @@ add_library(core STATIC hle/service/am/applets/profile_select.h hle/service/am/applets/software_keyboard.cpp hle/service/am/applets/software_keyboard.h + hle/service/am/applets/software_keyboard_types.h hle/service/am/applets/web_browser.cpp hle/service/am/applets/web_browser.h hle/service/am/applets/web_types.h diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 856ed33da..12c76c9ee 100755 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -1,29 +1,149 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/backend.h" +#include + +#include "common/logging/log.h" #include "common/string_util.h" #include "core/frontend/applets/software_keyboard.h" namespace Core::Frontend { + SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; -void DefaultSoftwareKeyboardApplet::RequestText( - std::function)> out, - SoftwareKeyboardParameters parameters) const { - if (parameters.initial_text.empty()) - out(u"yuzu"); +DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default; - out(parameters.initial_text); +void DefaultSoftwareKeyboardApplet::InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function submit_normal_callback_, + std::function + submit_inline_callback_) { + if (is_inline) { + LOG_WARNING( + Service_AM, + "(STUBBED) called, backend requested to initialize the inline software keyboard."); + + submit_inline_callback = std::move(submit_inline_callback_); + } else { + LOG_WARNING( + Service_AM, + "(STUBBED) called, backend requested to initialize the normal software keyboard."); + + submit_normal_callback = std::move(submit_normal_callback_); + } + + parameters = std::move(initialize_parameters); + + LOG_INFO(Service_AM, + "\nKeyboardInitializeParameters:" + "\nok_text={}" + "\nheader_text={}" + "\nsub_text={}" + "\nguide_text={}" + "\ninitial_text={}" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\ninitial_cursor_position={}" + "\ntype={}" + "\npassword_mode={}" + "\ntext_draw_type={}" + "\nkey_disable_flags={}" + "\nuse_blur_background={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text), + Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text), + Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length, + parameters.min_text_length, parameters.initial_cursor_position, parameters.type, + parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw, + parameters.use_blur_background, parameters.enable_backspace_button, + parameters.enable_return_button, parameters.disable_cancel_button); } -void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( - std::u16string error_message, std::function finished_check) const { +void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const { LOG_WARNING(Service_AM, - "(STUBBED) called - Default fallback software keyboard does not support text " - "check! (error_message={})", - Common::UTF16ToUTF8(error_message)); - finished_check(); + "(STUBBED) called, backend requested to show the normal software keyboard."); + + SubmitNormalText(u"yuzu"); } + +void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog."); +} + +void DefaultSoftwareKeyboardApplet::ShowInlineKeyboard( + InlineAppearParameters appear_parameters) const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to show the inline software keyboard."); + + LOG_INFO(Service_AM, + "\nInlineAppearParameters:" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\nkey_top_scale_x={}" + "\nkey_top_scale_y={}" + "\nkey_top_translate_x={}" + "\nkey_top_translate_y={}" + "\ntype={}" + "\nkey_disable_flags={}" + "\nkey_top_as_floating={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + appear_parameters.max_text_length, appear_parameters.min_text_length, + appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y, + appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y, + appear_parameters.type, appear_parameters.key_disable_flags.raw, + appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, + appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); + + std::thread([this] { SubmitInlineText(u"yuzu"); }).detach(); +} + +void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to hide the inline software keyboard."); +} + +void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_parameters) const { + LOG_WARNING(Service_AM, + "(STUBBED) called, backend requested to change the inline keyboard text."); + + LOG_INFO(Service_AM, + "\nInlineTextParameters:" + "\ninput_text={}" + "\ncursor_position={}", + Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); + + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + text_parameters.input_text, text_parameters.cursor_position); +} + +void DefaultSoftwareKeyboardApplet::ExitKeyboard() const { + LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to exit the software keyboard."); +} + +void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const { + submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text); +} + +void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + for (std::size_t index = 0; index < text.size(); ++index) { + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + std::u16string(text.data(), text.data() + index + 1), + static_cast(index) + 1); + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + + submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text), + static_cast(text.size())); +} + } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index f9b202664..506eb35bb 100755 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -1,54 +1,116 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include -#include -#include -#include "common/bit_field.h" +#include + #include "common/common_types.h" +#include "core/hle/service/am/applets/software_keyboard_types.h" + namespace Core::Frontend { -struct SoftwareKeyboardParameters { - std::u16string submit_text; + +struct KeyboardInitializeParameters { + std::u16string ok_text; std::u16string header_text; std::u16string sub_text; std::u16string guide_text; std::u16string initial_text; - std::size_t max_length; - bool password; - bool cursor_at_beginning; + u32 max_text_length; + u32 min_text_length; + s32 initial_cursor_position; + Service::AM::Applets::SwkbdType type; + Service::AM::Applets::SwkbdPasswordMode password_mode; + Service::AM::Applets::SwkbdTextDrawType text_draw_type; + Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + bool use_blur_background; + bool enable_backspace_button; + bool enable_return_button; + bool disable_cancel_button; +}; - union { - u8 value; +struct InlineAppearParameters { + u32 max_text_length; + u32 min_text_length; + f32 key_top_scale_x; + f32 key_top_scale_y; + f32 key_top_translate_x; + f32 key_top_translate_y; + Service::AM::Applets::SwkbdType type; + Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + bool key_top_as_floating; + bool enable_backspace_button; + bool enable_return_button; + bool disable_cancel_button; +}; - BitField<1, 1, u8> disable_space; - BitField<2, 1, u8> disable_address; - BitField<3, 1, u8> disable_percent; - BitField<4, 1, u8> disable_slash; - BitField<6, 1, u8> disable_number; - BitField<7, 1, u8> disable_download_code; - }; +struct InlineTextParameters { + std::u16string input_text; + s32 cursor_position; }; class SoftwareKeyboardApplet { public: virtual ~SoftwareKeyboardApplet(); - virtual void RequestText(std::function)> out, - SoftwareKeyboardParameters parameters) const = 0; - virtual void SendTextCheckDialog(std::u16string error_message, - std::function finished_check) const = 0; + virtual void InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function + submit_normal_callback_, + std::function + submit_inline_callback_) = 0; + + virtual void ShowNormalKeyboard() const = 0; + + virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const = 0; + + virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0; + + virtual void HideInlineKeyboard() const = 0; + + virtual void InlineTextChanged(InlineTextParameters text_parameters) const = 0; + + virtual void ExitKeyboard() const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: - void RequestText(std::function)> out, - SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message, - std::function finished_check) const override; + ~DefaultSoftwareKeyboardApplet() override; + + void InitializeKeyboard( + bool is_inline, KeyboardInitializeParameters initialize_parameters, + std::function + submit_normal_callback_, + std::function + submit_inline_callback_) override; + + void ShowNormalKeyboard() const override; + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const override; + + void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override; + + void HideInlineKeyboard() const override; + + void InlineTextChanged(InlineTextParameters text_parameters) const override; + + void ExitKeyboard() const override; + +private: + void SubmitNormalText(std::u16string text) const; + void SubmitInlineText(std::u16string_view text) const; + + KeyboardInitializeParameters parameters; + + mutable std::function + submit_normal_callback; + mutable std::function + submit_inline_callback; }; } // namespace Core::Frontend diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp index ec5fe660e..9f6a90e8f 100755 --- a/src/core/frontend/input_interpreter.cpp +++ b/src/core/frontend/input_interpreter.cpp @@ -12,7 +12,9 @@ InputInterpreter::InputInterpreter(Core::System& system) : npad{system.ServiceManager() .GetService("hid") ->GetAppletResource() - ->GetController(Service::HID::HidController::NPad)} {} + ->GetController(Service::HID::HidController::NPad)} { + ResetButtonStates(); +} InputInterpreter::~InputInterpreter() = default; @@ -25,6 +27,17 @@ void InputInterpreter::PollInput() { button_states[current_index] = button_state; } +void InputInterpreter::ResetButtonStates() { + previous_index = 0; + current_index = 0; + + button_states[0] = 0xFFFFFFFF; + + for (std::size_t i = 1; i < button_states.size(); ++i) { + button_states[i] = 0; + } +} + bool InputInterpreter::IsButtonPressed(HIDButton button) const { return (button_states[current_index] & (1U << static_cast(button))) != 0; } diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h index 73fc47ffb..9495e3daf 100755 --- a/src/core/frontend/input_interpreter.h +++ b/src/core/frontend/input_interpreter.h @@ -66,6 +66,9 @@ public: /// Gets a button state from HID and inserts it into the array of button states. void PollInput(); + /// Resets all the button states to their defaults. + void ResetButtonStates(); + /** * Checks whether the button is pressed. * diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 161d9f782..2b363b1d9 100755 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -75,10 +75,14 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_ if (incoming) { // Populate the object lists with the data in the IPC request. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { - copy_objects.push_back(handle_table.GetGeneric(rp.Pop())); + const u32 copy_handle{rp.Pop()}; + copy_handles.push_back(copy_handle); + copy_objects.push_back(handle_table.GetGeneric(copy_handle)); } for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) { - move_objects.push_back(handle_table.GetGeneric(rp.Pop())); + const u32 move_handle{rp.Pop()}; + move_handles.push_back(move_handle); + move_objects.push_back(handle_table.GetGeneric(move_handle)); } } else { // For responses we just ignore the handles, they're empty and will be populated when diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 9a769781b..6fba42615 100755 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -210,6 +210,14 @@ public: /// Helper function to test whether the output buffer at buffer_index can be written bool CanWriteBuffer(std::size_t buffer_index = 0) const; + Handle GetCopyHandle(std::size_t index) const { + return copy_handles.at(index); + } + + Handle GetMoveHandle(std::size_t index) const { + return move_handles.at(index); + } + template std::shared_ptr GetCopyObject(std::size_t index) { return DynamicObjectCast(copy_objects.at(index)); @@ -285,6 +293,8 @@ private: std::shared_ptr server_session; std::shared_ptr thread; // TODO(yuriks): Check common usage of this and optimize size accordingly + boost::container::small_vector move_handles; + boost::container::small_vector copy_handles; boost::container::small_vector, 8> move_objects; boost::container::small_vector, 8> copy_objects; boost::container::small_vector, 8> domain_objects; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8fd990577..f7d3f218a 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -67,8 +67,13 @@ struct KernelCore::Impl { is_phantom_mode_for_singlecore = false; InitializePhysicalCores(); - InitializeSystemResourceLimit(kernel, system); - InitializeMemoryLayout(); + + // Derive the initial memory layout from the emulated board + KMemoryLayout memory_layout; + DeriveInitialMemoryLayout(memory_layout); + InitializeMemoryLayout(memory_layout); + InitializeSystemResourceLimit(kernel, system, memory_layout); + InitializeSlabHeaps(); InitializeSchedulers(); InitializeSuspendThreads(); InitializePreemption(kernel); @@ -137,27 +142,32 @@ struct KernelCore::Impl { } // Creates the default system resource limit - void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) { + void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system, + const KMemoryLayout& memory_layout) { system_resource_limit = std::make_shared(kernel, system); + const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); // If setting the default system values fails, then something seriously wrong has occurred. - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000) + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) .IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) .IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); - // Derived from recent software updates. The kernel reserves 27MB - constexpr u64 kernel_size{0x1b00000}; - if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) { - UNREACHABLE(); - } // Reserve secure applet memory, introduced in firmware 5.0.0 - constexpr u64 secure_applet_memory_size{0x400000}; + constexpr u64 secure_applet_memory_size{Common::Size_4_MB}; ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, secure_applet_memory_size)); + + // This memory seems to be reserved on hardware, but is not reserved/used by yuzu. + // Likely Horizon OS reserved memory + // TODO(ameerj): Derive the memory rather than hardcode it. + constexpr u64 unknown_reserved_memory{0x2f896000}; + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + unknown_reserved_memory)); } void InitializePreemption(KernelCore& kernel) { @@ -531,11 +541,7 @@ struct KernelCore::Impl { linear_region_start); } - void InitializeMemoryLayout() { - // Derive the initial memory layout from the emulated board - KMemoryLayout memory_layout; - DeriveInitialMemoryLayout(memory_layout); - + void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); @@ -578,11 +584,14 @@ struct KernelCore::Impl { system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize}, KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size, "Time:SharedMemory"); + } + void InitializeSlabHeaps() { // Allocate slab heaps user_slab_heap_pages = std::make_unique>(); - constexpr u64 user_slab_heap_size{0x1ef000}; + // TODO(ameerj): This should be derived, not hardcoded within the kernel + constexpr u64 user_slab_heap_size{0x3de000}; // Reserve slab heaps ASSERT( system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 9d5956ead..dd01f3924 100755 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -120,9 +120,7 @@ std::shared_ptr Process::Create(Core::System& system, std::string name, std::shared_ptr process = std::make_shared(system); process->name = std::move(name); - // TODO: This is inaccurate - // The process should hold a reference to the kernel-wide resource limit. - process->resource_limit = std::make_shared(kernel, system); + process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -160,17 +158,13 @@ void Process::DecrementThreadCount() { } u64 Process::GetTotalPhysicalMemoryAvailable() const { - // TODO: This is expected to always return the application memory pool size after accurately - // reserving kernel resources. The current workaround uses a process-local resource limit of - // application memory pool size, which is inaccurate. const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; - + ASSERT(capacity == kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); if (capacity < memory_usage_capacity) { return capacity; } - return memory_usage_capacity; } @@ -272,10 +266,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { @@ -324,16 +314,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, UNREACHABLE(); } - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application)); - - resource_limit->SetLimitValue(LimitableResource::Threads, 608); - resource_limit->SetLimitValue(LimitableResource::Events, 700); - resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128); - resource_limit->SetLimitValue(LimitableResource::Sessions, 894); - // Create TLS region tls_region_address = CreateTLSRegion(); memory_reservation.Commit(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4374487a3..b5cef7f9f 100755 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -971,7 +971,7 @@ private: auto storage = applet->GetBroker().PopNormalDataToGame(); if (storage == nullptr) { - LOG_ERROR(Service_AM, + LOG_DEBUG(Service_AM, "storage is a nullptr. There is no data in the current normal channel"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); @@ -1002,7 +1002,7 @@ private: auto storage = applet->GetBroker().PopInteractiveDataToGame(); if (storage == nullptr) { - LOG_ERROR(Service_AM, + LOG_DEBUG(Service_AM, "storage is a nullptr. There is no data in the current interactive channel"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); @@ -1125,7 +1125,7 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) {2, nullptr, "AreAnyLibraryAppletsLeft"}, {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, - {12, nullptr, "CreateHandleStorage"}, + {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, }; RegisterHandlers(functions); } @@ -1134,14 +1134,15 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default; void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const auto applet_id = rp.PopRaw(); - const auto applet_mode = rp.PopRaw(); + const auto applet_mode = rp.PopRaw(); LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, applet_mode); const auto& applet_manager{system.GetAppletManager()}; - const auto applet = applet_manager.GetApplet(applet_id); + const auto applet = applet_manager.GetApplet(applet_id, applet_mode); if (applet == nullptr) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); @@ -1159,9 +1160,18 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 size{rp.Pop()}; + + const s64 size{rp.Pop()}; + LOG_DEBUG(Service_AM, "called, size={}", size); + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + std::vector buffer(size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -1170,18 +1180,65 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { } void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - IPC::RequestParser rp{ctx}; - rp.SetCurrentOffset(3); - const auto handle{rp.Pop()}; + struct Parameters { + u8 permissions; + s64 size; + }; + + const auto parameters{rp.PopRaw()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions, + parameters.size, handle); + + if (parameters.size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } auto transfer_mem = system.CurrentProcess()->GetHandleTable().Get(handle); if (transfer_mem == nullptr) { - LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + + const u8* const mem_begin = transfer_mem->GetPointer(); + const u8* const mem_end = mem_begin + transfer_mem->GetSize(); + std::vector memory{mem_begin, mem_end}; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(system, std::move(memory)); +} + +void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s64 size{rp.Pop()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_UNKNOWN); + return; + } + + auto transfer_mem = + system.CurrentProcess()->GetHandleTable().Get(handle); + + if (transfer_mem == nullptr) { + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_UNKNOWN); return; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index f6a453ab7..aefbdf0d5 100755 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -254,6 +254,7 @@ private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); + void CreateHandleStorage(Kernel::HLERequestContext& ctx); }; class IApplicationFunctions final : public ServiceFramework { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index e2f3b7563..5ddad851a 100755 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -241,31 +241,31 @@ void AppletManager::ClearAll() { frontend = {}; } -std::shared_ptr AppletManager::GetApplet(AppletId id) const { +std::shared_ptr AppletManager::GetApplet(AppletId id, LibraryAppletMode mode) const { switch (id) { case AppletId::Auth: - return std::make_shared(system, *frontend.parental_controls); + return std::make_shared(system, mode, *frontend.parental_controls); case AppletId::Controller: - return std::make_shared(system, *frontend.controller); + return std::make_shared(system, mode, *frontend.controller); case AppletId::Error: - return std::make_shared(system, *frontend.error); + return std::make_shared(system, mode, *frontend.error); case AppletId::ProfileSelect: - return std::make_shared(system, *frontend.profile_select); + return std::make_shared(system, mode, *frontend.profile_select); case AppletId::SoftwareKeyboard: - return std::make_shared(system, *frontend.software_keyboard); + return std::make_shared(system, mode, *frontend.software_keyboard); case AppletId::Web: case AppletId::Shop: case AppletId::OfflineWeb: case AppletId::LoginShare: case AppletId::WebAuth: - return std::make_shared(system, *frontend.web_browser); + return std::make_shared(system, mode, *frontend.web_browser); case AppletId::PhotoViewer: - return std::make_shared(system, *frontend.photo_viewer); + return std::make_shared(system, mode, *frontend.photo_viewer); default: UNIMPLEMENTED_MSG( "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", static_cast(id)); - return std::make_shared(system, id); + return std::make_shared(system, id, mode); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index b9a006317..26b482015 100755 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -62,6 +62,14 @@ enum class AppletId : u32 { MyPage = 0x1A, }; +enum class LibraryAppletMode : u32 { + AllForeground = 0, + Background = 1, + NoUI = 2, + BackgroundIndirectDisplay = 3, + AllForegroundInitiallyHidden = 4, +}; + class AppletDataBroker final { public: explicit AppletDataBroker(Kernel::KernelCore& kernel_); @@ -200,7 +208,7 @@ public: void SetDefaultAppletsIfMissing(); void ClearAll(); - std::shared_ptr GetApplet(AppletId id) const; + std::shared_ptr GetApplet(AppletId id, LibraryAppletMode mode) const; private: AppletFrontendSet frontend; diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp index c2bfe698f..a33f05f97 100755 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/controller.cpp @@ -45,8 +45,9 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters( }; } -Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Controller::~Controller() = default; diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h index d4c9da7b1..07cb92bf9 100755 --- a/src/core/hle/service/am/applets/controller.h +++ b/src/core/hle/service/am/applets/controller.h @@ -106,7 +106,8 @@ static_assert(sizeof(ControllerSupportResultInfo) == 0xC, class Controller final : public Applet { public: - explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_); + explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_); ~Controller() override; void Initialize() override; @@ -119,6 +120,7 @@ public: void ConfigurationComplete(); private: + LibraryAppletMode applet_mode; const Core::Frontend::ControllerApplet& frontend; Core::System& system; diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index 0c8b632e8..a9f0a9c95 100755 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -86,8 +86,9 @@ ResultCode Decode64BitError(u64 error) { } // Anonymous namespace -Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Error::~Error() = default; diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h index a105cdb0c..a3e520cd4 100755 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/error.h @@ -25,7 +25,8 @@ enum class ErrorAppletMode : u8 { class Error final : public Applet { public: - explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); + explicit Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_); ~Error() override; void Initialize() override; @@ -40,6 +41,7 @@ public: private: union ErrorArguments; + LibraryAppletMode applet_mode; const Core::Frontend::ErrorApplet& frontend; ResultCode error_code = RESULT_SUCCESS; ErrorAppletMode mode = ErrorAppletMode::ShowError; diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 4d1df5cbe..71016cce7 100755 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -37,8 +37,9 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) } } -Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} Auth::~Auth() = default; @@ -152,8 +153,9 @@ void Auth::AuthFinished(bool is_successful) { broker.SignalStateChanged(); } -PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} PhotoViewer::~PhotoViewer() = default; @@ -202,8 +204,8 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet(Core::System& system_, AppletId id_) - : Applet{system_.Kernel()}, id{id_}, system{system_} {} +StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) + : Applet{system_.Kernel()}, id{id_}, applet_mode{applet_mode_}, system{system_} {} StubApplet::~StubApplet() = default; diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index ba76ae3d3..d9e6d4384 100755 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h @@ -20,7 +20,8 @@ enum class AuthAppletType : u32 { class Auth final : public Applet { public: - explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_); + explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_); ~Auth() override; void Initialize() override; @@ -32,6 +33,7 @@ public: void AuthFinished(bool is_successful = true); private: + LibraryAppletMode applet_mode; Core::Frontend::ParentalControlsApplet& frontend; Core::System& system; bool complete = false; @@ -50,7 +52,8 @@ enum class PhotoViewerAppletMode : u8 { class PhotoViewer final : public Applet { public: - explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); + explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_); ~PhotoViewer() override; void Initialize() override; @@ -62,6 +65,7 @@ public: void ViewFinished(); private: + LibraryAppletMode applet_mode; const Core::Frontend::PhotoViewerApplet& frontend; bool complete = false; PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; @@ -70,7 +74,7 @@ private: class StubApplet final : public Applet { public: - explicit StubApplet(Core::System& system_, AppletId id_); + explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_); ~StubApplet() override; void Initialize() override; @@ -82,6 +86,7 @@ public: private: AppletId id; + LibraryAppletMode applet_mode; Core::System& system; }; diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 77fba16c7..ab8b6fcc5 100755 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,9 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect(Core::System& system_, +ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} ProfileSelect::~ProfileSelect() = default; diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 648d33a24..90f054030 100755 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -33,7 +33,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - explicit ProfileSelect(Core::System& system_, + explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_); ~ProfileSelect() override; @@ -47,6 +47,7 @@ public: void SelectionComplete(std::optional uuid); private: + LibraryAppletMode applet_mode; const Core::Frontend::ProfileSelectApplet& frontend; UserSelectionConfig config; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 79b209c6b..c3a05de9c 100755 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -1,93 +1,80 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include "common/assert.h" #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/software_keyboard.h" -#include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/software_keyboard.h" namespace Service::AM::Applets { namespace { -enum class Request : u32 { - Finalize = 0x4, - SetUserWordInfo = 0x6, - SetCustomizeDic = 0x7, - Calc = 0xa, - SetCustomizedDictionaries = 0xb, - UnsetCustomizedDictionaries = 0xc, - UnknownD = 0xd, - UnknownE = 0xe, -}; -constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; -constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; -constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; -constexpr std::size_t DEFAULT_MAX_LENGTH = 500; -constexpr bool INTERACTIVE_STATUS_OK = false; -} // Anonymous namespace -static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( - KeyboardConfig config, std::u16string initial_text) { - Core::Frontend::SoftwareKeyboardParameters params{}; - params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - config.submit_text.data(), config.submit_text.size()); - params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - config.header_text.data(), config.header_text.size()); - params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), - config.sub_text.size()); - params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), - config.guide_text.size()); - params.initial_text = std::move(initial_text); - params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; - params.password = static_cast(config.is_password); - params.cursor_at_beginning = static_cast(config.initial_cursor_position); - params.value = static_cast(config.keyset_disable_bitmask); +// The maximum number of UTF-16 characters that can be input into the swkbd text field. +constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; - return params; +constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); +constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; +constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; + +constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + return "Success"; + case SwkbdTextCheckResult::Failure: + return "Failure"; + case SwkbdTextCheckResult::Confirm: + return "Confirm"; + case SwkbdTextCheckResult::Silent: + return "Silent"; + default: + UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); + return "Unknown"; + } } -SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, - const Core::Frontend::SoftwareKeyboardApplet& frontend_) - : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} +void SetReplyBase(std::vector& reply, SwkbdState state, SwkbdReplyType reply_type) { + std::memcpy(reply.data(), &state, sizeof(SwkbdState)); + std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); +} + +} // Anonymous namespace + +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {} SoftwareKeyboard::~SoftwareKeyboard() = default; void SoftwareKeyboard::Initialize() { - complete = false; - is_inline = false; - initial_text.clear(); - final_data.clear(); - Applet::Initialize(); - const auto keyboard_config_storage = broker.PopNormalDataToApplet(); - ASSERT(keyboard_config_storage != nullptr); - const auto& keyboard_config = keyboard_config_storage->GetData(); + LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", + applet_mode); - if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) { - is_inline = true; - return; + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; + + switch (applet_mode) { + case LibraryAppletMode::AllForeground: + InitializeForeground(); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + InitializeBackground(applet_mode); + break; + default: + UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + break; } - - ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); - std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); - - const auto work_buffer_storage = broker.PopNormalDataToApplet(); - ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); - const auto& work_buffer = work_buffer_storage->GetData(); - - if (config.initial_string_size == 0) - return; - - std::vector string(config.initial_string_size); - std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, - string.size() * 2); - initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); } bool SoftwareKeyboard::TransactionComplete() const { @@ -95,106 +82,996 @@ bool SoftwareKeyboard::TransactionComplete() const { } ResultCode SoftwareKeyboard::GetStatus() const { - return RESULT_SUCCESS; + return status; } void SoftwareKeyboard::ExecuteInteractive() { - if (complete) + if (complete) { return; + } - const auto storage = broker.PopInteractiveDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - if (!is_inline) { - const auto status = static_cast(data[0]); - if (status == INTERACTIVE_STATUS_OK) { - complete = true; - } else { - std::array string; - std::memcpy(string.data(), data.data() + 4, string.size() * 2); - frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), - [this] { broker.SignalStateChanged(); }); - } + if (is_background) { + ProcessInlineKeyboardRequest(); } else { - Request request{}; - std::memcpy(&request, data.data(), sizeof(Request)); - - switch (request) { - case Request::Finalize: - complete = true; - broker.SignalStateChanged(); - break; - case Request::Calc: { - broker.PushNormalDataFromApplet(std::make_shared(system, std::vector{1})); - broker.SignalStateChanged(); - break; - } - default: - UNIMPLEMENTED_MSG("Request {:X} is not implemented", request); - break; - } + ProcessTextCheck(); } } void SoftwareKeyboard::Execute() { if (complete) { - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(final_data))); - broker.SignalStateChanged(); return; } - const auto parameters = ConvertToFrontendParameters(config, initial_text); - if (!is_inline) { - frontend.RequestText( - [this](std::optional text) { WriteText(std::move(text)); }, parameters); + if (is_background) { + return; } + + ShowNormalKeyboard(); } -void SoftwareKeyboard::WriteText(std::optional text) { - std::vector output_main(SWKBD_OUTPUT_BUFFER_SIZE); +void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { + if (complete) { + return; + } - if (text.has_value()) { - std::vector output_sub(SWKBD_OUTPUT_BUFFER_SIZE); - - if (config.utf_8) { - const u64 size = text->size() + sizeof(u64); - const auto new_text = Common::UTF16ToUTF8(*text); - - std::memcpy(output_sub.data(), &size, sizeof(u64)); - std::memcpy(output_sub.data() + 8, new_text.data(), - std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); - - output_main[0] = INTERACTIVE_STATUS_OK; - std::memcpy(output_main.data() + 4, new_text.data(), - std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); - } else { - const u64 size = text->size() * 2 + sizeof(u64); - std::memcpy(output_sub.data(), &size, sizeof(u64)); - std::memcpy(output_sub.data() + 8, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); - - output_main[0] = INTERACTIVE_STATUS_OK; - std::memcpy(output_main.data() + 4, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); - } - - complete = !config.text_check; - final_data = output_main; - - if (complete) { - broker.PushNormalDataFromApplet( - std::make_shared(system, std::move(output_main))); - broker.SignalStateChanged(); - } else { - broker.PushInteractiveDataFromApplet( - std::make_shared(system, std::move(output_sub))); - } + if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { + SubmitForTextCheck(submitted_text); } else { - output_main[0] = 1; - complete = true; - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(output_main))); - broker.SignalStateChanged(); + SubmitNormalOutputAndExit(result, submitted_text); } } + +void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + if (complete) { + return; + } + + current_text = std::move(submitted_text); + current_cursor_position = cursor_position; + + if (inline_use_utf8) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringUtf8; + break; + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorUtf8; + break; + case SwkbdReplyType::DecidedEnter: + reply_type = SwkbdReplyType::DecidedEnterUtf8; + break; + default: + break; + } + } + + if (use_changed_string_v2) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringV2; + break; + case SwkbdReplyType::ChangedStringUtf8: + reply_type = SwkbdReplyType::ChangedStringUtf8V2; + break; + default: + break; + } + } + + if (use_moved_cursor_v2) { + switch (reply_type) { + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorV2; + break; + case SwkbdReplyType::MovedCursorUtf8: + reply_type = SwkbdReplyType::MovedCursorUtf8V2; + break; + default: + break; + } + } + + SendReply(reply_type); +} + +void SoftwareKeyboard::InitializeForeground() { + LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); + + is_background = false; + + const auto swkbd_config_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_config_storage != nullptr); + + const auto& swkbd_config_data = swkbd_config_storage->GetData(); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); + + std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); + + switch (swkbd_applet_version) { + case SwkbdAppletVersion::Version5: + case SwkbdAppletVersion::Version65542: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); + std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld)); + break; + case SwkbdAppletVersion::Version196615: + case SwkbdAppletVersion::Version262152: + case SwkbdAppletVersion::Version327689: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); + std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld2)); + break; + case SwkbdAppletVersion::Version393227: + case SwkbdAppletVersion::Version524301: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, + swkbd_config_data.size()); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + } + + const auto work_buffer_storage = broker.PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + + if (swkbd_config_common.initial_string_length == 0) { + InitializeFrontendKeyboard(); + return; + } + + const auto& work_buffer = work_buffer_storage->GetData(); + + std::vector initial_string(swkbd_config_common.initial_string_length); + + std::memcpy(initial_string.data(), + work_buffer.data() + swkbd_config_common.initial_string_offset, + swkbd_config_common.initial_string_length * sizeof(char16_t)); + + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), + initial_string.size()); + + LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); + + InitializeFrontendKeyboard(); +} + +void SoftwareKeyboard::InitializeBackground(LibraryAppletMode applet_mode) { + LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); + + is_background = true; + + const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_inline_initialize_arg_storage != nullptr); + + const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); + ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); + + std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), + swkbd_inline_initialize_arg.size()); + + if (swkbd_initialize_arg.library_applet_mode_flag) { + ASSERT(applet_mode == LibraryAppletMode::Background); + } else { + ASSERT(applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + } +} + +void SoftwareKeyboard::ProcessTextCheck() { + const auto text_check_storage = broker.PopInteractiveDataToApplet(); + ASSERT(text_check_storage != nullptr); + + const auto& text_check_data = text_check_storage->GetData(); + ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); + + SwkbdTextCheck swkbd_text_check; + + std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); + + std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size()); + + LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", + GetTextCheckResultName(swkbd_text_check.text_check_result), + Common::UTF16ToUTF8(text_check_message)); + + switch (swkbd_text_check.text_check_result) { + case SwkbdTextCheckResult::Success: + SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); + break; + case SwkbdTextCheckResult::Failure: + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message); + break; + case SwkbdTextCheckResult::Confirm: + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message); + break; + case SwkbdTextCheckResult::Silent: + default: + break; + } +} + +void SoftwareKeyboard::ProcessInlineKeyboardRequest() { + const auto request_data_storage = broker.PopInteractiveDataToApplet(); + ASSERT(request_data_storage != nullptr); + + const auto& request_data = request_data_storage->GetData(); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); + + SwkbdRequestCommand request_command; + + std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); + + switch (request_command) { + case SwkbdRequestCommand::Finalize: + RequestFinalize(request_data); + break; + case SwkbdRequestCommand::SetUserWordInfo: + RequestSetUserWordInfo(request_data); + break; + case SwkbdRequestCommand::SetCustomizeDic: + RequestSetCustomizeDic(request_data); + break; + case SwkbdRequestCommand::Calc: + RequestCalc(request_data); + break; + case SwkbdRequestCommand::SetCustomizedDictionaries: + RequestSetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::UnsetCustomizedDictionaries: + RequestUnsetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::SetChangedStringV2Flag: + RequestSetChangedStringV2Flag(request_data); + break; + case SwkbdRequestCommand::SetMovedCursorV2Flag: + RequestSetMovedCursorV2Flag(request_data); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); + break; + } +} + +void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, + std::u16string submitted_text) { + std::vector out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); + + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, + utf8_submitted_text); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, + Common::UTF16ToUTF8(submitted_text)); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), + submitted_text.size() * sizeof(char16_t)); + } + + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + + ExitKeyboard(); +} + +void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { + current_text = std::move(submitted_text); + + std::vector out_data(sizeof(u64) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); + const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, + utf8_submitted_text); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, + Common::UTF16ToUTF8(current_text)); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), current_text.data(), + current_text.size() * sizeof(char16_t)); + } + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(out_data))); +} + +void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { + switch (reply_type) { + case SwkbdReplyType::FinishedInitialize: + ReplyFinishedInitialize(); + break; + case SwkbdReplyType::Default: + ReplyDefault(); + break; + case SwkbdReplyType::ChangedString: + ReplyChangedString(); + break; + case SwkbdReplyType::MovedCursor: + ReplyMovedCursor(); + break; + case SwkbdReplyType::MovedTab: + ReplyMovedTab(); + break; + case SwkbdReplyType::DecidedEnter: + ReplyDecidedEnter(); + break; + case SwkbdReplyType::DecidedCancel: + ReplyDecidedCancel(); + break; + case SwkbdReplyType::ChangedStringUtf8: + ReplyChangedStringUtf8(); + break; + case SwkbdReplyType::MovedCursorUtf8: + ReplyMovedCursorUtf8(); + break; + case SwkbdReplyType::DecidedEnterUtf8: + ReplyDecidedEnterUtf8(); + break; + case SwkbdReplyType::UnsetCustomizeDic: + ReplyUnsetCustomizeDic(); + break; + case SwkbdReplyType::ReleasedUserWordInfo: + ReplyReleasedUserWordInfo(); + break; + case SwkbdReplyType::UnsetCustomizedDictionaries: + ReplyUnsetCustomizedDictionaries(); + break; + case SwkbdReplyType::ChangedStringV2: + ReplyChangedStringV2(); + break; + case SwkbdReplyType::MovedCursorV2: + ReplyMovedCursorV2(); + break; + case SwkbdReplyType::ChangedStringUtf8V2: + ReplyChangedStringUtf8V2(); + break; + case SwkbdReplyType::MovedCursorUtf8V2: + ReplyMovedCursorUtf8V2(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); + ReplyDefault(); + break; + } +} + +void SoftwareKeyboard::ChangeState(SwkbdState state) { + swkbd_state = state; + + ReplyDefault(); +} + +void SoftwareKeyboard::InitializeFrontendKeyboard() { + if (is_background) { + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = + current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{ok_text}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); + } else { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box + ? swkbd_config_common.enable_return_button + : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{ok_text}, + .header_text{header_text}, + .sub_text{sub_text}, + .guide_text{guide_text}, + .initial_text{initial_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard(false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text) { + SubmitTextNormal(result, submitted_text); + }, + {}); + } +} + +void SoftwareKeyboard::ShowNormalKeyboard() { + frontend.ShowNormalKeyboard(); +} + +void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + frontend.ShowTextCheckDialog(text_check_result, text_check_message); +} + +void SoftwareKeyboard::ShowInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::HideInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsShown) { + return; + } + + ChangeState(SwkbdState::InitializedIsDisappearing); + + frontend.HideInlineKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); +} + +void SoftwareKeyboard::InlineTextChanged() { + Core::Frontend::InlineTextParameters text_parameters{ + .input_text{current_text}, + .cursor_position{current_cursor_position}, + }; + + frontend.InlineTextChanged(std::move(text_parameters)); +} + +void SoftwareKeyboard::ExitKeyboard() { + complete = true; + status = RESULT_SUCCESS; + + frontend.ExitKeyboard(); + + broker.SignalStateChanged(); +} + +// Inline Software Keyboard Requests + +void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Finalize"); + + ChangeState(SwkbdState::NotInitialized); + + ExitKeyboard(); +} + +void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); +} + +void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); +} + +void SoftwareKeyboard::RequestCalc(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Calc"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + + std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArg)); + + if (swkbd_calc_arg.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + } + + if (swkbd_calc_arg.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg.cursor_position; + } + + if (swkbd_calc_arg.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { + InitializeFrontendKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg.flags.set_initialize_arg && + (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { + ShowInlineKeyboard(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); +} + +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); + + ReplyUnsetCustomizedDictionaries(); +} + +void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +// Inline Software Keyboard Replies + +void SoftwareKeyboard::ReplyFinishedInitialize() { + LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); + + std::vector reply(REPLY_BASE_SIZE + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDefault() { + LOG_DEBUG(Service_AM, "Sending Reply: Default"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedString() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursor() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedTab() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); + + const SwkbdMovedTabArg moved_tab_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, + sizeof(SwkbdMovedTabArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnter() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyDecidedCancel() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnterUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyUnsetCustomizeDic() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyReleasedUserWordInfo() { + LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringV2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorV2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 1d260fef8..85aeb4eb1 100755 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -1,20 +1,14 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include -#include -#include - #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/service/am/am.h" +#include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" - -union ResultCode; +#include "core/hle/service/am/applets/software_keyboard_types.h" namespace Core { class System; @@ -22,45 +16,10 @@ class System; namespace Service::AM::Applets { -enum class KeysetDisable : u32 { - Space = 0x02, - Address = 0x04, - Percent = 0x08, - Slashes = 0x10, - Numbers = 0x40, - DownloadCode = 0x80, -}; - -struct KeyboardConfig { - INSERT_PADDING_BYTES(4); - std::array submit_text; - u16_le left_symbol_key; - u16_le right_symbol_key; - INSERT_PADDING_BYTES(1); - KeysetDisable keyset_disable_bitmask; - u32_le initial_cursor_position; - std::array header_text; - std::array sub_text; - std::array guide_text; - u32_le length_limit; - INSERT_PADDING_BYTES(4); - u32_le is_password; - INSERT_PADDING_BYTES(5); - bool utf_8; - bool draw_background; - u32_le initial_string_offset; - u32_le initial_string_size; - u32_le user_dictionary_offset; - u32_le user_dictionary_size; - bool text_check; - u64_le text_check_callback; -}; -static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); - class SoftwareKeyboard final : public Applet { public: - explicit SoftwareKeyboard(Core::System& system_, - const Core::Frontend::SoftwareKeyboardApplet& frontend_); + explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_); ~SoftwareKeyboard() override; void Initialize() override; @@ -70,17 +29,139 @@ public: void ExecuteInteractive() override; void Execute() override; - void WriteText(std::optional text); + /** + * Submits the input text to the application. + * If text checking is enabled, the application will verify the input text. + * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the normal software keyboard. + * + * @param result SwkbdResult enum + * @param submitted_text UTF-16 encoded string + */ + void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); + + /** + * Submits the input text to the application. + * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the inline software keyboard. + * + * @param reply_type SwkbdReplyType enum + * @param submitted_text UTF-16 encoded string + * @param cursor_position The current position of the text cursor + */ + void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position); private: - const Core::Frontend::SoftwareKeyboardApplet& frontend; + /// Initializes the normal software keyboard. + void InitializeForeground(); - KeyboardConfig config; - std::u16string initial_text; - bool complete = false; - bool is_inline = false; - std::vector final_data; + /// Initializes the inline software keyboard. + void InitializeBackground(LibraryAppletMode applet_mode); + + /// Processes the text check sent by the application. + void ProcessTextCheck(); + + /// Processes the inline software keyboard request command sent by the application. + void ProcessInlineKeyboardRequest(); + + /// Submits the input text and exits the applet. + void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); + + /// Submits the input text for text checking. + void SubmitForTextCheck(std::u16string submitted_text); + + /// Sends a reply to the application after processing a request command. + void SendReply(SwkbdReplyType reply_type); + + /// Changes the inline keyboard state. + void ChangeState(SwkbdState state); + + /** + * Signals the frontend to initialize the software keyboard with common parameters. + * This initializes either the normal software keyboard or the inline software keyboard + * depending on the state of is_background. + * Note that this does not cause the keyboard to appear. + * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + */ + void InitializeFrontendKeyboard(); + + /// Signals the frontend to show the normal software keyboard. + void ShowNormalKeyboard(); + + /// Signals the frontend to show the text check dialog. + void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + /// Signals the frontend to show the inline software keyboard. + void ShowInlineKeyboard(); + + /// Signals the frontend to hide the inline software keyboard. + void HideInlineKeyboard(); + + /// Signals the frontend that the current inline keyboard text has changed. + void InlineTextChanged(); + + /// Signals both the frontend and application that the software keyboard is exiting. + void ExitKeyboard(); + + // Inline Software Keyboard Requests + + void RequestFinalize(const std::vector& request_data); + void RequestSetUserWordInfo(const std::vector& request_data); + void RequestSetCustomizeDic(const std::vector& request_data); + void RequestCalc(const std::vector& request_data); + void RequestSetCustomizedDictionaries(const std::vector& request_data); + void RequestUnsetCustomizedDictionaries(const std::vector& request_data); + void RequestSetChangedStringV2Flag(const std::vector& request_data); + void RequestSetMovedCursorV2Flag(const std::vector& request_data); + + // Inline Software Keyboard Replies + + void ReplyFinishedInitialize(); + void ReplyDefault(); + void ReplyChangedString(); + void ReplyMovedCursor(); + void ReplyMovedTab(); + void ReplyDecidedEnter(); + void ReplyDecidedCancel(); + void ReplyChangedStringUtf8(); + void ReplyMovedCursorUtf8(); + void ReplyDecidedEnterUtf8(); + void ReplyUnsetCustomizeDic(); + void ReplyReleasedUserWordInfo(); + void ReplyUnsetCustomizedDictionaries(); + void ReplyChangedStringV2(); + void ReplyMovedCursorV2(); + void ReplyChangedStringUtf8V2(); + void ReplyMovedCursorUtf8V2(); + + LibraryAppletMode applet_mode; + Core::Frontend::SoftwareKeyboardApplet& frontend; Core::System& system; + + SwkbdAppletVersion swkbd_applet_version; + + SwkbdConfigCommon swkbd_config_common; + SwkbdConfigOld swkbd_config_old; + SwkbdConfigOld2 swkbd_config_old2; + SwkbdConfigNew swkbd_config_new; + std::u16string initial_text; + + SwkbdState swkbd_state{SwkbdState::NotInitialized}; + SwkbdInitializeArg swkbd_initialize_arg; + SwkbdCalcArg swkbd_calc_arg; + bool use_changed_string_v2{false}; + bool use_moved_cursor_v2{false}; + bool inline_use_utf8{false}; + s32 current_cursor_position{}; + + std::u16string current_text; + + bool is_background{false}; + + bool complete{false}; + ResultCode status{RESULT_SUCCESS}; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/software_keyboard_types.h index 288d52aa0..21aa8e800 100755 --- a/src/core/hle/service/am/applets/software_keyboard_types.h +++ b/src/core/hle/service/am/applets/software_keyboard_types.h @@ -176,7 +176,7 @@ struct SwkbdConfigNew { std::array customized_dictionary_set_entries{}; u8 total_customized_dictionary_set_entries{}; bool disable_cancel_button{}; - INSERT_PADDING_BYTES(15); + INSERT_PADDING_BYTES(18); }; static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon), "SwkbdConfigNew has incorrect size."); diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 2ab420789..b28b849bc 100755 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -208,8 +208,9 @@ void ExtractSharedFonts(Core::System& system) { } // namespace -WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_) - : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} +WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_) + : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend(frontend_), system{system_} {} WebBrowser::~WebBrowser() = default; diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index 04c274754..5eafbae7b 100755 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -25,7 +25,8 @@ namespace Service::AM::Applets { class WebBrowser final : public Applet { public: - WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_); + WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_); ~WebBrowser() override; @@ -63,6 +64,7 @@ private: void ExecuteWifi(); void ExecuteLobby(); + LibraryAppletMode applet_mode; const Core::Frontend::WebBrowserApplet& frontend; bool complete{false}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index c8031970b..b9d30927a 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -36,6 +36,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}}; system.GPU().PushCommandBuffer(cmdlist); + system.GPU().MemoryManager().InvalidateQueuedCaches(); } return UnmapBuffer(input, output); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index c2f152190..6654aa66d 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -198,7 +198,13 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector& input, std::vec return NvResult::InvalidState; } if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) { - gpu.MemoryManager().Unmap(object->dma_map_addr, *size); + if (vic_device) { + // UnmapVicFrame defers texture_cache invalidation of the frame address until + // the stream is over + gpu.MemoryManager().UnmapVicFrame(object->dma_map_addr, *size); + } else { + gpu.MemoryManager().Unmap(object->dma_map_addr, *size); + } } else { // This occurs quite frequently, however does not seem to impact functionality LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 4c9d4ba41..2e0aece2f 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -160,6 +160,7 @@ protected: s32_le nvmap_fd{}; u32_le submit_timeout{}; + bool vic_device{}; std::shared_ptr nvmap_dev; SyncpointManager& syncpoint_manager; std::array device_syncpoints{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 0421fb956..6c2f95fc0 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -12,8 +12,9 @@ namespace Service::Nvidia::Devices { nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr nvmap_dev, SyncpointManager& syncpoint_manager) - : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {} - + : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) { + vic_device = true; +} nvhost_vic::~nvhost_vic() = default; NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index f67de37e3..51c26c6c3 100755 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -761,7 +761,7 @@ std::vector SDLState::GetInputDevices() { for (const auto& joystick : value) { if (auto* const controller = joystick->GetSDLGameController()) { std::string name = - fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); + fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, {"display", std::move(name)}, @@ -782,6 +782,17 @@ std::vector SDLState::GetInputDevices() { return devices; } +std::string SDLState::GetControllerName(SDL_GameController* controller) const { + switch (SDL_GameControllerGetType(controller)) { + case SDL_CONTROLLER_TYPE_XBOX360: + return "XBox 360 Controller"; + case SDL_CONTROLLER_TYPE_XBOXONE: + return "XBox One Controller"; + default: + return SDL_GameControllerName(controller); + } +} + namespace { Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, float value = 0.1f) { @@ -930,16 +941,19 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return {}; } + const bool invert = + SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. // We will add those afterwards // This list also excludes Screenshot since theres not really a mapping for that using ButtonBindings = std::array, 17>; - static constexpr ButtonBindings switch_to_sdl_button{{ - {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, - {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, + const ButtonBindings switch_to_sdl_button{{ + {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 08044b00d..8b7363f56 100755 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -14,6 +14,7 @@ #include "input_common/sdl/sdl.h" union SDL_Event; +using SDL_GameController = struct _SDL_GameController; using SDL_Joystick = struct _SDL_Joystick; using SDL_JoystickID = s32; @@ -64,6 +65,9 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); + /// Returns a custom name for specific controllers because the default name is not correct + std::string GetControllerName(SDL_GameController* controller) const; + // Set to true if SDL supports game controller subsystem bool has_gamecontroller = false; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 8b45f1b62..5d659dcaf 100755 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1785,6 +1785,8 @@ public: SSY, SYNC, BRK, + CAL, + RET, DEPBAR, VOTE, VOTE_VTG, @@ -2108,6 +2110,8 @@ private: INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"), INST("111000110100----", Id::BRK, Type::Flow, "BRK"), INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"), + INST("111000100110----", Id::CAL, Type::Flow, "CAL"), + INST("111000110010----", Id::RET, Type::Flow, "RET"), INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"), INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"), diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 4eb71efbd..9529a1256 100755 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -114,6 +114,25 @@ void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) { .IsSuccess()); } +void MemoryManager::UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size) { + if (!size) { + return; + } + + const std::optional cpu_addr = GpuToCpuAddress(gpu_addr); + ASSERT(cpu_addr); + rasterizer->InvalidateExceptTextureCache(*cpu_addr, size); + cache_invalidate_queue.push_back({*cpu_addr, size}); + + UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); +} + +void MemoryManager::InvalidateQueuedCaches() { + for (const auto& entry : cache_invalidate_queue) { + rasterizer->InvalidateTextureCache(entry.first, entry.second); + } + cache_invalidate_queue.clear(); +} PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { return page_table[PageEntryIndex(gpu_addr)]; } diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index b3538d503..5d6c196fa 100755 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -123,6 +123,14 @@ public: [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); void Unmap(GPUVAddr gpu_addr, std::size_t size); + /** + * Some Decoded NVDEC frames require that texture cache does not get invalidated. + * UnmapVicFrame defers the texture cache invalidation until the stream ends + * by invoking InvalidateQueuedCaches to invalidate all frame texture caches. + */ + void UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size); + void InvalidateQueuedCaches(); + private: [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 50491b758..afc7ff4ae 100755 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -69,6 +69,12 @@ public: /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory virtual void FlushRegion(VAddr addr, u64 size) = 0; + /// Notify rasterizer to flush the texture cache to Switch memory + virtual void InvalidateExceptTextureCache(VAddr addr, u64 size) = 0; + + /// Notify rasterizer to invalidate the texture cache + virtual void InvalidateTextureCache(VAddr addr, u64 size) = 0; + /// Check if the the specified memory area requires flushing to CPU Memory. virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp index 3e4d88c30..e98647478 100755 --- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp @@ -491,6 +491,9 @@ private: const Registry& registry; const ShaderType stage; + std::shared_ptr context_func; + u32 ast_var_base{}; + std::size_t num_temporaries = 0; std::size_t max_temporaries = 0; @@ -807,13 +810,33 @@ ARBDecompiler::ARBDecompiler(const Device& device_, const ShaderIR& ir_, const R : device{device_}, ir{ir_}, registry{registry_}, stage{stage_} { DefineGlobalMemory(); + context_func = ir.GetMainFunction(); + ast_var_base = 0; + AddLine("TEMP RC;"); AddLine("TEMP FSWZA[4];"); AddLine("TEMP FSWZB[4];"); - if (ir.IsDecompiled()) { + InitializeVariables(); + AddLine("main:"); + if (context_func->IsDecompiled()) { DecompileAST(); } else { DecompileBranchMode(); + AddLine("RET;"); + } + + const auto& subfunctions = ir.GetSubFunctions(); + auto it = subfunctions.begin(); + while (it != subfunctions.end()) { + context_func = *it; + AddLine("func_{}:", context_func->GetId()); + if (context_func->IsDecompiled()) { + DecompileAST(); + } else { + DecompileBranchMode(); + AddLine("RET;"); + } + it++; } AddLine("END"); @@ -1060,41 +1083,38 @@ void ARBDecompiler::InitializeVariables() { } void ARBDecompiler::DecompileAST() { - const u32 num_flow_variables = ir.GetASTNumVariables(); + const u32 num_flow_variables = context_func->GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; ++i) { - AddLine("TEMP F{};", i); + AddLine("TEMP F{};", i + ast_var_base); } for (u32 i = 0; i < num_flow_variables; ++i) { - AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i); + AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i + ast_var_base); } - InitializeVariables(); - - VisitAST(ir.GetASTProgram()); + VisitAST(context_func->GetASTProgram()); + ast_var_base += num_flow_variables; } void ARBDecompiler::DecompileBranchMode() { static constexpr u32 FLOW_STACK_SIZE = 20; - if (!ir.IsFlowStackDisabled()) { + if (!context_func->IsFlowStackDisabled()) { AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE); AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE); AddLine("TEMP SSY_TOP;"); AddLine("TEMP PBK_TOP;"); } - AddLine("TEMP PC;"); + AddLine("TEMP PC{};", context_func->GetId()); - if (!ir.IsFlowStackDisabled()) { + if (!context_func->IsFlowStackDisabled()) { AddLine("MOV.U SSY_TOP.x, 0;"); AddLine("MOV.U PBK_TOP.x, 0;"); } - InitializeVariables(); - - const auto basic_block_end = ir.GetBasicBlocks().end(); - auto basic_block_it = ir.GetBasicBlocks().begin(); + const auto basic_block_end = context_func->GetBasicBlocks().end(); + auto basic_block_it = context_func->GetBasicBlocks().begin(); const u32 first_address = basic_block_it->first; - AddLine("MOV.U PC.x, {};", first_address); + AddLine("MOV.U PC{}.x, {};", context_func->GetId(), first_address); AddLine("REP;"); @@ -1103,7 +1123,7 @@ void ARBDecompiler::DecompileBranchMode() { const auto& [address, bb] = *basic_block_it; ++num_blocks; - AddLine("SEQ.S.CC RC.x, PC.x, {};", address); + AddLine("SEQ.S.CC RC.x, PC{}.x, {};", context_func->GetId(), address); AddLine("IF NE.x;"); VisitBlock(bb); @@ -1114,7 +1134,7 @@ void ARBDecompiler::DecompileBranchMode() { const auto op = std::get_if(&*bb[bb.size() - 1]); if (!op || op->GetCode() != OperationCode::Branch) { const u32 next_address = basic_block_it->first; - AddLine("MOV.U PC.x, {};", next_address); + AddLine("MOV.U PC{}.x, {};", context_func->GetId(), next_address); AddLine("CONT;"); } } @@ -1152,7 +1172,8 @@ void ARBDecompiler::VisitAST(const ASTNode& node) { } else if (const auto decoded = std::get_if(&*node->GetInnerData())) { VisitBlock(decoded->nodes); } else if (const auto var_set = std::get_if(&*node->GetInnerData())) { - AddLine("MOV.U F{}, {};", var_set->index, VisitExpression(var_set->condition)); + AddLine("MOV.U F{}, {};", var_set->index + ast_var_base, + VisitExpression(var_set->condition)); ResetTemporaries(); } else if (const auto do_while = std::get_if(&*node->GetInnerData())) { const std::string condition = VisitExpression(do_while->condition); @@ -1172,7 +1193,11 @@ void ARBDecompiler::VisitAST(const ASTNode& node) { ResetTemporaries(); } if (ast_return->kills) { - AddLine("KIL TR;"); + if (stage == ShaderType::Fragment) { + AddLine("KIL TR;"); + } else { + AddLine("RET;"); + } } else { Exit(); } @@ -1219,7 +1244,7 @@ std::string ARBDecompiler::VisitExpression(const Expr& node) { return Visit(ir.GetConditionCode(expr->cc)); } if (const auto expr = std::get_if(&*node)) { - return fmt::format("F{}.x", expr->var_index); + return fmt::format("F{}.x", expr->var_index + ast_var_base); } if (const auto expr = std::get_if(&*node)) { return expr->value ? "0xffffffff" : "0"; @@ -1406,6 +1431,11 @@ std::string ARBDecompiler::Visit(const Node& node) { return {}; } + if (const auto func_call = std::get_if(&*node)) { + AddLine("CAL func_{};", func_call->GetFuncId()); + return {}; + } + if ([[maybe_unused]] const auto cmt = std::get_if(&*node)) { // Uncommenting this will generate invalid code. GLASM lacks comments. // AddLine("// {}", cmt->GetText()); @@ -1479,7 +1509,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) { } void ARBDecompiler::Exit() { - if (stage != ShaderType::Fragment) { + if (!context_func->IsMain() || stage != ShaderType::Fragment) { AddLine("RET;"); return; } @@ -2021,13 +2051,13 @@ std::string ARBDecompiler::ImageStore(Operation operation) { std::string ARBDecompiler::Branch(Operation operation) { const auto target = std::get(*operation[0]); - AddLine("MOV.U PC.x, {};", target.GetValue()); + AddLine("MOV.U PC{}.x, {};", context_func->GetId(), target.GetValue()); AddLine("CONT;"); return {}; } std::string ARBDecompiler::BranchIndirect(Operation operation) { - AddLine("MOV.U PC.x, {};", Visit(operation[0])); + AddLine("MOV.U PC{}.x, {};", context_func->GetId(), Visit(operation[0])); AddLine("CONT;"); return {}; } @@ -2045,7 +2075,7 @@ std::string ARBDecompiler::PopFlowStack(Operation operation) { const auto stack = std::get(operation.GetMeta()); const std::string_view stack_name = StackName(stack); AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name); - AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name); + AddLine("MOV.U PC{}.x, {}[{}_TOP.x].x;", context_func->GetId(), stack_name, stack_name); AddLine("CONT;"); return {}; } @@ -2056,6 +2086,10 @@ std::string ARBDecompiler::Exit(Operation) { } std::string ARBDecompiler::Discard(Operation) { + if (stage != ShaderType::Fragment) { + AddLine("RET;"); + return {}; + } AddLine("KIL TR;"); return {}; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4610fd160..a7f7733eb 100755 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -544,6 +544,26 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { query_cache.FlushRegion(addr, size); } +void RasterizerOpenGL::InvalidateExceptTextureCache(VAddr addr, u64 size) { + if (addr == 0 || size == 0) { + return; + } + shader_cache.InvalidateRegion(addr, size); + { + std::scoped_lock lock{buffer_cache.mutex}; + buffer_cache.WriteMemory(addr, size); + } + query_cache.InvalidateRegion(addr, size); +} + +void RasterizerOpenGL::InvalidateTextureCache(VAddr addr, u64 size) { + if (addr == 0 || size == 0) { + return; + } + std::scoped_lock lock{texture_cache.mutex}; + texture_cache.UnmapMemory(addr, size); +} + bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; if (!Settings::IsGPULevelHigh()) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 3745cf637..9730544d9 100755 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -74,6 +74,8 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void FlushAll() override; void FlushRegion(VAddr addr, u64 size) override; + void InvalidateExceptTextureCache(VAddr addr, u64 size) override; + void InvalidateTextureCache(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index ac78d344c..fd49014b9 100755 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -79,6 +79,11 @@ const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f ); const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f ); )"; +enum class HelperFunction { + SignedAtomic = 0, + Total, +}; + class ShaderWriter final { public: void AddExpression(std::string_view text) { @@ -434,6 +439,28 @@ public: DeclareInternalFlags(); DeclareCustomVariables(); DeclarePhysicalAttributeReader(); + DeclareHelpersForward(); + + const auto& subfunctions = ir.GetSubFunctions(); + auto it = subfunctions.rbegin(); + while (it != subfunctions.rend()) { + context_func = *it; + code.AddLine("void func_{}() {{", context_func->GetId()); + ++code.scope; + + if (context_func->IsDecompiled()) { + DecompileAST(); + } else { + DecompileBranchMode(); + } + + --code.scope; + code.AddLine("}}"); + + it++; + } + + context_func = ir.GetMainFunction(); code.AddLine("void main() {{"); ++code.scope; @@ -442,7 +469,7 @@ public: code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);"); } - if (ir.IsDecompiled()) { + if (context_func->IsDecompiled()) { DecompileAST(); } else { DecompileBranchMode(); @@ -450,6 +477,9 @@ public: --code.scope; code.AddLine("}}"); + + code.AddNewLine(); + DeclareHelpers(); } std::string GetResult() { @@ -462,13 +492,13 @@ private: void DecompileBranchMode() { // VM's program counter - const auto first_address = ir.GetBasicBlocks().begin()->first; + const auto first_address = context_func->GetBasicBlocks().begin()->first; code.AddLine("uint jmp_to = {}U;", first_address); // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems // unlikely that shaders will use 20 nested SSYs and PBKs. constexpr u32 FLOW_STACK_SIZE = 20; - if (!ir.IsFlowStackDisabled()) { + if (!context_func->IsFlowStackDisabled()) { for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); @@ -480,7 +510,7 @@ private: code.AddLine("switch (jmp_to) {{"); - for (const auto& pair : ir.GetBasicBlocks()) { + for (const auto& pair : context_func->GetBasicBlocks()) { const auto& [address, bb] = pair; code.AddLine("case 0x{:X}U: {{", address); ++code.scope; @@ -599,7 +629,7 @@ private: size = limit; } - code.AddLine("shared uint smem[{}];", size / 4); + code.AddLine("shared uint {}[{}];", GetSharedMemory(), size / 4); code.AddNewLine(); } code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;", @@ -983,6 +1013,27 @@ private: } } + void DeclareHelpersForward() { + code.AddLine("int Helpers_AtomicShared(uint offset, int value, bool is_min);"); + code.AddNewLine(); + } + + void DeclareHelpers() { + if (IsHelperEnabled(HelperFunction::SignedAtomic)) { + code.AddLine( + R"(int Helpers_AtomicShared(uint offset, int value, bool is_min) {{ + uint oldValue, newValue; + do {{ + oldValue = {}[offset]; + newValue = is_min ? uint(min(int(oldValue), value)) : uint(max(int(oldValue), value)); + }} while (atomicCompSwap({}[offset], newValue, oldValue) != oldValue); + return int(oldValue); +}})", + GetSharedMemory(), GetSharedMemory()); + code.AddNewLine(); + } + } + void VisitBlock(const NodeBlock& bb) { for (const auto& node : bb) { Visit(node).CheckVoid(); @@ -1109,7 +1160,9 @@ private: } if (const auto smem = std::get_if(&*node)) { - return {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint}; + return { + fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()), + Type::Uint}; } if (const auto internal_flag = std::get_if(&*node)) { @@ -1131,6 +1184,11 @@ private: return {}; } + if (const auto func_call = std::get_if(&*node)) { + code.AddLine("func_{}();", func_call->GetFuncId()); + return {}; + } + if (const auto comment = std::get_if(&*node)) { code.AddLine("// " + comment->GetText()); return {}; @@ -1598,7 +1656,9 @@ private: Type::Uint}; } else if (const auto smem = std::get_if(&*dest)) { ASSERT(stage == ShaderType::Compute); - target = {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint}; + target = { + fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()), + Type::Uint}; } else if (const auto gmem = std::get_if(&*dest)) { const std::string real = Visit(gmem->GetRealAddress()).AsUint(); const std::string base = Visit(gmem->GetBaseAddress()).AsUint(); @@ -2115,7 +2175,14 @@ private: UNIMPLEMENTED_IF(meta->sampler.is_array); const std::size_t count = operation.GetOperandsCount(); - std::string expr = "texelFetch("; + std::string expr = "texelFetch"; + + if (!meta->aoffi.empty()) { + expr += "Offset"; + } + + expr += '('; + expr += GetSampler(meta->sampler); expr += ", "; @@ -2137,6 +2204,20 @@ private: expr += ", "; expr += Visit(meta->lod).AsInt(); } + + if (!meta->aoffi.empty()) { + expr += ", "; + expr += constructors.at(meta->aoffi.size() - 1); + expr += '('; + for (size_t i = 0; i < meta->aoffi.size(); ++i) { + if (i > 0) { + expr += ", "; + } + expr += Visit(meta->aoffi[i]).AsInt(); + } + expr += ')'; + } + expr += ')'; expr += GetSwizzle(meta->element); @@ -2183,8 +2264,11 @@ private: template Expression Atomic(Operation operation) { if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) { - UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations"); - return {}; + // Use a helper as a workaround due to memory being uint + SetHelperEnabled(HelperFunction::SignedAtomic, true); + return {fmt::format("Helpers_AtomicShared({}, {}, {})", Visit(operation[0]).AsInt(), + Visit(operation[1]).AsInt(), opname == Func::Min), + Type::Int}; } return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), Visit(operation[1]).AsUint()), @@ -2267,7 +2351,9 @@ private: } Expression Exit(Operation operation) { - PreExit(); + if (context_func->IsMain()) { + PreExit(); + } code.AddLine("return;"); return {}; } @@ -2277,7 +2363,11 @@ private: // about unexecuted instructions that may follow this. code.AddLine("if (true) {{"); ++code.scope; - code.AddLine("discard;"); + if (stage != ShaderType::Fragment) { + code.AddLine("return;"); + } else { + code.AddLine("discard;"); + } --code.scope; code.AddLine("}}"); return {}; @@ -2388,7 +2478,7 @@ private: } Expression Barrier(Operation) { - if (!ir.IsDecompiled()) { + if (!context_func->IsDecompiled()) { LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled"); return {}; } @@ -2705,6 +2795,10 @@ private: } } + constexpr std::string_view GetSharedMemory() const { + return "shared_mem"; + } + std::string GetInternalFlag(InternalFlag flag) const { constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag", "overflow_flag"}; @@ -2746,6 +2840,14 @@ private: return std::min(device.GetMaxVaryings(), Maxwell::NumVaryings); } + void SetHelperEnabled(HelperFunction hf, bool enabled) { + helper_functions_enabled[static_cast(hf)] = enabled; + } + + bool IsHelperEnabled(HelperFunction hf) const { + return helper_functions_enabled[static_cast(hf)]; + } + const Device& device; const ShaderIR& ir; const Registry& registry; @@ -2755,9 +2857,13 @@ private: const Header header; std::unordered_map transform_feedback; + std::shared_ptr context_func; + ShaderWriter code; std::optional max_input_vertices; + + std::array(HelperFunction::Total)> helper_functions_enabled{}; }; std::string GetFlowVariable(u32 index) { @@ -2902,9 +3008,15 @@ public: decomp.code.scope++; } if (ast.kills) { - decomp.code.AddLine("discard;"); + if (decomp.stage != ShaderType::Fragment) { + decomp.code.AddLine("return;"); + } else { + decomp.code.AddLine("discard;"); + } } else { - decomp.PreExit(); + if (decomp.context_func->IsMain()) { + decomp.PreExit(); + } decomp.code.AddLine("return;"); } if (!is_true) { @@ -2937,13 +3049,13 @@ private: }; void GLSLDecompiler::DecompileAST() { - const u32 num_flow_variables = ir.GetASTNumVariables(); + const u32 num_flow_variables = context_func->GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; i++) { code.AddLine("bool {} = false;", GetFlowVariable(i)); } ASTDecompiler decompiler{*this}; - decompiler.Visit(ir.GetASTProgram()); + decompiler.Visit(context_func->GetASTProgram()); } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index df5b7b172..69a0faca3 100755 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -493,6 +493,26 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { query_cache.FlushRegion(addr, size); } +void Vulkan::RasterizerVulkan::InvalidateExceptTextureCache(VAddr addr, u64 size) { + if (addr == 0 || size == 0) { + return; + } + pipeline_cache.InvalidateRegion(addr, size); + { + std::scoped_lock lock{buffer_cache.mutex}; + buffer_cache.WriteMemory(addr, size); + } + query_cache.InvalidateRegion(addr, size); +} + +void Vulkan::RasterizerVulkan::InvalidateTextureCache(VAddr addr, u64 size) { + if (addr == 0 || size == 0) { + return; + } + std::scoped_lock lock{texture_cache.mutex}; + texture_cache.UnmapMemory(addr, size); +} + bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; if (!Settings::IsGPULevelHigh()) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 235afc6f3..d8b5bf0fc 100755 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -66,6 +66,8 @@ public: void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void FlushAll() override; void FlushRegion(VAddr addr, u64 size) override; + void InvalidateExceptTextureCache(VAddr addr, u64 size) override; + void InvalidateTextureCache(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override; diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index c6846d886..258e2f5df 100755 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -406,10 +406,38 @@ private: binding = DeclareStorageTexels(binding); binding = DeclareImages(binding); + const auto& subfunctions = ir.GetSubFunctions(); + + labels.resize(subfunctions.size() + 1); + other_functions.resize(subfunctions.size()); + + auto it = subfunctions.rbegin(); + while (it != subfunctions.rend()) { + context_func = *it; + other_functions[context_func->GetId() - 1] = + OpFunction(t_void, {}, TypeFunction(t_void)); + AddLabel(); + + if (context_func->IsDecompiled()) { + DeclareFlowVariables(); + DecompileAST(); + } else { + AllocateLabels(); + DecompileBranchMode(); + } + + OpReturn(); + OpFunctionEnd(); + + it++; + } + + context_func = ir.GetMainFunction(); + const Id main = OpFunction(t_void, {}, TypeFunction(t_void)); AddLabel(); - if (ir.IsDecompiled()) { + if (context_func->IsDecompiled()) { DeclareFlowVariables(); DecompileAST(); } else { @@ -441,16 +469,18 @@ private: void DecompileAST(); void DecompileBranchMode() { - const u32 first_address = ir.GetBasicBlocks().begin()->first; - const Id loop_label = OpLabel("loop"); - const Id merge_label = OpLabel("merge"); + const u32 first_address = context_func->GetBasicBlocks().begin()->first; + const u32 func_id = context_func->GetId(); + const std::string func_id_msg = std::to_string(func_id); + const Id loop_label = OpLabel("loop_" + func_id_msg); + const Id merge_label = OpLabel("merge_" + func_id_msg); const Id dummy_label = OpLabel(); const Id jump_label = OpLabel(); - continue_label = OpLabel("continue"); + continue_label = OpLabel("continue_" + func_id_msg); std::vector literals; std::vector branch_labels; - for (const auto& [literal, label] : labels) { + for (const auto& [literal, label] : labels[func_id]) { literals.push_back(literal); branch_labels.push_back(label); } @@ -462,11 +492,11 @@ private: std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack(); std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack(); - Name(jmp_to, "jmp_to"); - Name(ssy_flow_stack, "ssy_flow_stack"); - Name(ssy_flow_stack_top, "ssy_flow_stack_top"); - Name(pbk_flow_stack, "pbk_flow_stack"); - Name(pbk_flow_stack_top, "pbk_flow_stack_top"); + Name(jmp_to, "jmp_to_" + func_id_msg); + Name(ssy_flow_stack, "ssy_flow_stack_" + func_id_msg); + Name(ssy_flow_stack_top, "ssy_flow_stack_top_" + func_id_msg); + Name(pbk_flow_stack, "pbk_flow_stack_" + func_id_msg); + Name(pbk_flow_stack_top, "pbk_flow_stack_top_" + func_id_msg); DefinePrologue(); @@ -484,13 +514,14 @@ private: AddLabel(default_branch); OpReturn(); - for (const auto& [address, bb] : ir.GetBasicBlocks()) { - AddLabel(labels.at(address)); + for (const auto& [address, bb] : context_func->GetBasicBlocks()) { + AddLabel(labels[func_id].at(address)); VisitBasicBlock(bb); - const auto next_it = labels.lower_bound(address + 1); - const Id next_label = next_it != labels.end() ? next_it->second : default_branch; + const auto next_it = labels[func_id].lower_bound(address + 1); + const Id next_label = + next_it != labels[func_id].end() ? next_it->second : default_branch; OpBranch(next_label); } @@ -508,9 +539,10 @@ private: static constexpr auto INTERNAL_FLAGS_COUNT = static_cast(InternalFlag::Amount); void AllocateLabels() { - for (const auto& pair : ir.GetBasicBlocks()) { + const u32 func_id = context_func->GetId(); + for (const auto& pair : context_func->GetBasicBlocks()) { const u32 address = pair.first; - labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address))); + labels[func_id].emplace(address, OpLabel(fmt::format("label_0x{:x}", address))); } } @@ -589,6 +621,14 @@ private: DeclareOutputVertex(); } + void SafeKill() { + if (stage != ShaderType::Fragment) { + OpReturn(); + return; + } + OpKill(); + } + void DeclareFragment() { if (stage != ShaderType::Fragment) { return; @@ -656,7 +696,7 @@ private: } void DeclareFlowVariables() { - for (u32 i = 0; i < ir.GetASTNumVariables(); i++) { + for (u32 i = 0; i < context_func->GetASTNumVariables(); i++) { const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); Name(id, fmt::format("flow_var_{}", static_cast(i))); flow_variables.emplace(i, AddGlobalVariable(id)); @@ -1333,6 +1373,12 @@ private: return {}; } + if (const auto func_call = std::get_if(&*node)) { + const u32 func_id = func_call->GetFuncId(); + OpFunctionCall(t_void, other_functions[func_id - 1]); + return {}; + } + if (const auto comment = std::get_if(&*node)) { if (device.HasDebuggingToolAttached()) { // We should insert comments with OpString instead of using named variables @@ -2124,7 +2170,7 @@ private: OpBranchConditional(condition, true_label, discard_label); AddLabel(discard_label); - OpKill(); + SafeKill(); AddLabel(true_label); } @@ -2175,7 +2221,9 @@ private: } Expression Exit(Operation operation) { - PreExit(); + if (context_func->IsMain()) { + PreExit(); + } inside_branch = true; if (conditional_branch_set) { OpReturn(); @@ -2192,12 +2240,12 @@ private: Expression Discard(Operation operation) { inside_branch = true; if (conditional_branch_set) { - OpKill(); + SafeKill(); } else { const Id dummy = OpLabel(); OpBranch(dummy); AddLabel(dummy); - OpKill(); + SafeKill(); AddLabel(); } return {}; @@ -2276,7 +2324,7 @@ private: } Expression Barrier(Operation) { - if (!ir.IsDecompiled()) { + if (!context_func->IsDecompiled()) { LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled"); return {}; } @@ -2770,6 +2818,8 @@ private: const Specialization& specialization; std::unordered_map transform_feedback; + std::shared_ptr context_func; + const Id t_void = Name(TypeVoid(), "void"); const Id t_bool = Name(TypeBool(), "bool"); @@ -2896,7 +2946,8 @@ private: Id ssy_flow_stack{}; Id pbk_flow_stack{}; Id continue_label{}; - std::map labels; + std::vector> labels; + std::vector other_functions; bool conditional_branch_set{}; bool inside_branch{}; @@ -3047,9 +3098,11 @@ public: decomp.OpBranchConditional(condition, then_label, endif_label); decomp.AddLabel(then_label); if (ast.kills) { - decomp.OpKill(); + decomp.SafeKill(); } else { - decomp.PreExit(); + if (decomp.context_func->IsMain()) { + decomp.PreExit(); + } decomp.OpReturn(); } decomp.AddLabel(endif_label); @@ -3058,9 +3111,11 @@ public: decomp.OpBranch(next_block); decomp.AddLabel(next_block); if (ast.kills) { - decomp.OpKill(); + decomp.SafeKill(); } else { - decomp.PreExit(); + if (decomp.context_func->IsMain()) { + decomp.PreExit(); + } decomp.OpReturn(); } decomp.AddLabel(decomp.OpLabel()); @@ -3097,7 +3152,7 @@ private: }; void SPIRVDecompiler::DecompileAST() { - const u32 num_flow_variables = ir.GetASTNumVariables(); + const u32 num_flow_variables = context_func->GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; i++) { const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); Name(id, fmt::format("flow_var_{}", i)); @@ -3106,7 +3161,7 @@ void SPIRVDecompiler::DecompileAST() { DefinePrologue(); - const ASTNode program = ir.GetASTProgram(); + const ASTNode program = context_func->GetASTProgram(); ASTDecompiler decompiler{*this}; decompiler.Visit(program); diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 43d965f2f..7c8bd7e2f 100755 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "common/assert.h" @@ -26,17 +27,29 @@ using Tegra::Shader::OpCode; constexpr s32 unassigned_branch = -2; +enum class JumpLabel : u32 { + SSYClass = 0, + PBKClass = 1, +}; + +struct JumpItem { + JumpLabel type; + u32 address; + + bool operator==(const JumpItem& other) const { + return std::tie(type, address) == std::tie(other.type, other.address); + } +}; + struct Query { u32 address{}; - std::stack ssy_stack{}; - std::stack pbk_stack{}; + std::stack stack{}; }; struct BlockStack { BlockStack() = default; - explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} - std::stack ssy_stack{}; - std::stack pbk_stack{}; + explicit BlockStack(const Query& q) : stack{q.stack} {} + std::stack stack{}; }; template @@ -65,20 +78,36 @@ struct BlockInfo { } }; -struct CFGRebuildState { - explicit CFGRebuildState(const ProgramCode& program_code_, u32 start_, Registry& registry_) - : program_code{program_code_}, registry{registry_}, start{start_} {} +struct ProgramControl { + std::unordered_set found_functions{}; + std::list pending_functions{}; + void RegisterFunction(u32 address) { + if (found_functions.count(address) != 0) { + return; + } + found_functions.insert(address); + pending_functions.emplace_back(address); + } +}; + +struct CFGRebuildState { + explicit CFGRebuildState(ProgramControl& control_, const ProgramCode& program_code_, u32 start_, + u32 base_start_, Registry& registry_) + : control{control_}, program_code{program_code_}, registry{registry_}, start{start_}, + base_start{base_start_} {} + + ProgramControl& control; const ProgramCode& program_code; Registry& registry; u32 start{}; + u32 base_start{}; std::vector block_info; std::list inspect_queries; std::list queries; std::unordered_map registered; std::set labels; - std::map ssy_labels; - std::map pbk_labels; + std::map jump_labels; std::unordered_map stacks; ASTManager* manager{}; }; @@ -153,7 +182,7 @@ template std::optional TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test, PackCallable pack) { for (; pos >= state.start; --pos) { - if (IsSchedInstruction(pos, state.start)) { + if (IsSchedInstruction(pos, state.base_start)) { continue; } const Instruction instr = state.program_code[pos]; @@ -262,7 +291,7 @@ std::pair ParseCode(CFGRebuildState& state, u32 address) single_branch.ignore = true; break; } - if (IsSchedInstruction(offset, state.start)) { + if (IsSchedInstruction(offset, state.base_start)) { offset++; continue; } @@ -274,6 +303,7 @@ std::pair ParseCode(CFGRebuildState& state, u32 address) } switch (opcode->get().GetId()) { + case OpCode::Id::RET: case OpCode::Id::EXIT: { const auto pred_index = static_cast(instr.pred.pred_index); single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0); @@ -411,13 +441,20 @@ std::pair ParseCode(CFGRebuildState& state, u32 address) case OpCode::Id::SSY: { const u32 target = offset + instr.bra.GetBranchTarget(); insert_label(state, target); - state.ssy_labels.emplace(offset, target); + JumpItem it = {JumpLabel::SSYClass, target}; + state.jump_labels.emplace(offset, it); break; } case OpCode::Id::PBK: { const u32 target = offset + instr.bra.GetBranchTarget(); insert_label(state, target); - state.pbk_labels.emplace(offset, target); + JumpItem it = {JumpLabel::PBKClass, target}; + state.jump_labels.emplace(offset, it); + break; + } + case OpCode::Id::CAL: { + const u32 target = offset + instr.bra.GetBranchTarget(); + state.control.RegisterFunction(target); break; } case OpCode::Id::BRX: { @@ -513,7 +550,7 @@ bool TryInspectAddress(CFGRebuildState& state) { } bool TryQuery(CFGRebuildState& state) { - const auto gather_labels = [](std::stack& cc, std::map& labels, + const auto gather_labels = [](std::stack& cc, std::map& labels, BlockInfo& block) { auto gather_start = labels.lower_bound(block.start); const auto gather_end = labels.upper_bound(block.end); @@ -522,6 +559,19 @@ bool TryQuery(CFGRebuildState& state) { ++gather_start; } }; + const auto pop_labels = [](JumpLabel type, SingleBranch* branch, Query& query) -> bool { + while (!query.stack.empty() && query.stack.top().type != type) { + query.stack.pop(); + } + if (query.stack.empty()) { + return false; + } + if (branch->address == unassigned_branch) { + branch->address = query.stack.top().address; + } + query.stack.pop(); + return true; + }; if (state.queries.empty()) { return false; } @@ -534,8 +584,7 @@ bool TryQuery(CFGRebuildState& state) { // consumes a label. Schedule new queries accordingly if (block.visited) { BlockStack& stack = state.stacks[q.address]; - const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) && - (stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack); + const bool all_okay = (stack.stack.empty() || q.stack == stack.stack); state.queries.pop_front(); return all_okay; } @@ -544,8 +593,7 @@ bool TryQuery(CFGRebuildState& state) { Query q2(q); state.queries.pop_front(); - gather_labels(q2.ssy_stack, state.ssy_labels, block); - gather_labels(q2.pbk_stack, state.pbk_labels, block); + gather_labels(q2.stack, state.jump_labels, block); if (std::holds_alternative(*block.branch)) { auto* branch = std::get_if(block.branch.get()); if (!branch->condition.IsUnconditional()) { @@ -555,16 +603,10 @@ bool TryQuery(CFGRebuildState& state) { auto& conditional_query = state.queries.emplace_back(q2); if (branch->is_sync) { - if (branch->address == unassigned_branch) { - branch->address = conditional_query.ssy_stack.top(); - } - conditional_query.ssy_stack.pop(); + pop_labels(JumpLabel::SSYClass, branch, conditional_query); } if (branch->is_brk) { - if (branch->address == unassigned_branch) { - branch->address = conditional_query.pbk_stack.top(); - } - conditional_query.pbk_stack.pop(); + pop_labels(JumpLabel::PBKClass, branch, conditional_query); } conditional_query.address = branch->address; return true; @@ -646,25 +688,23 @@ void DecompileShader(CFGRebuildState& state) { state.manager->Decompile(); } -} // Anonymous namespace - -std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 start_address, - const CompilerSettings& settings, - Registry& registry) { - auto result_out = std::make_unique(); +ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_code, + u32 start_address, u32 base_start, const CompilerSettings& settings, + Registry& registry) { + ShaderFunction result_out{}; if (settings.depth == CompileDepth::BruteForce) { - result_out->settings.depth = CompileDepth::BruteForce; + result_out.settings.depth = CompileDepth::BruteForce; return result_out; } - CFGRebuildState state{program_code, start_address, registry}; + CFGRebuildState state{control, program_code, start_address, base_start, registry}; // Inspect Code and generate blocks state.labels.clear(); state.labels.emplace(start_address); state.inspect_queries.push_back(state.start); while (!state.inspect_queries.empty()) { if (!TryInspectAddress(state)) { - result_out->settings.depth = CompileDepth::BruteForce; + result_out.settings.depth = CompileDepth::BruteForce; return result_out; } } @@ -675,7 +715,7 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, if (settings.depth != CompileDepth::FlowStack) { // Decompile Stacks - state.queries.push_back(Query{state.start, {}, {}}); + state.queries.push_back(Query{state.start, {}}); decompiled = true; while (!state.queries.empty()) { if (!TryQuery(state)) { @@ -705,19 +745,18 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, state.manager->ShowCurrentState("Of Shader"); state.manager->Clear(); } else { - auto characteristics = std::make_unique(); - characteristics->start = start_address; - characteristics->settings.depth = settings.depth; - characteristics->manager = std::move(manager); - characteristics->end = state.block_info.back().end + 1; - return characteristics; + result_out.start = start_address; + result_out.settings.depth = settings.depth; + result_out.manager = std::move(manager); + result_out.end = state.block_info.back().end + 1; + return result_out; } } - result_out->start = start_address; - result_out->settings.depth = + result_out.start = start_address; + result_out.settings.depth = use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack; - result_out->blocks.clear(); + result_out.blocks.clear(); for (auto& block : state.block_info) { ShaderBlock new_block{}; new_block.start = block.start; @@ -726,20 +765,20 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, if (!new_block.ignore_branch) { new_block.branch = block.branch; } - result_out->end = std::max(result_out->end, block.end); - result_out->blocks.push_back(new_block); + result_out.end = std::max(result_out.end, block.end); + result_out.blocks.push_back(new_block); } if (!use_flow_stack) { - result_out->labels = std::move(state.labels); + result_out.labels = std::move(state.labels); return result_out; } - auto back = result_out->blocks.begin(); + auto back = result_out.blocks.begin(); auto next = std::next(back); - while (next != result_out->blocks.end()) { + while (next != result_out.blocks.end()) { if (!state.labels.contains(next->start) && next->start == back->end + 1) { back->end = next->end; - next = result_out->blocks.erase(next); + next = result_out.blocks.erase(next); continue; } back = next; @@ -748,4 +787,22 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, return result_out; } + +} // Anonymous namespace + +std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 start_address, + const CompilerSettings& settings, Registry& registry) { + ProgramControl control{}; + auto result_out = std::make_unique(); + result_out->main = + ScanFunction(control, program_code, start_address, start_address, settings, registry); + while (!control.pending_functions.empty()) { + u32 address = control.pending_functions.front(); + auto fun = ScanFunction(control, program_code, address, start_address, settings, registry); + result_out->subfunctions.emplace(address, std::move(fun)); + control.pending_functions.pop_front(); + } + return result_out; +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 37bf96492..5ef2251b9 100755 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -101,7 +102,7 @@ struct ShaderBlock { } }; -struct ShaderCharacteristics { +struct ShaderFunction { std::list blocks{}; std::set labels{}; u32 start{}; @@ -110,8 +111,12 @@ struct ShaderCharacteristics { CompilerSettings settings{}; }; -std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 start_address, - const CompilerSettings& settings, - Registry& registry); +struct ShaderProgram { + ShaderFunction main; + std::map subfunctions; +}; + +std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 start_address, + const CompilerSettings& settings, Registry& registry); } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 6576d1208..355c724a3 100755 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -64,9 +64,52 @@ std::optional TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce, } // Anonymous namespace +class ExprDecoder { +public: + explicit ExprDecoder(ShaderIR& ir_) : ir(ir_) {} + + void operator()(const ExprAnd& expr) { + Visit(expr.operand1); + Visit(expr.operand2); + } + + void operator()(const ExprOr& expr) { + Visit(expr.operand1); + Visit(expr.operand2); + } + + void operator()(const ExprNot& expr) { + Visit(expr.operand1); + } + + void operator()(const ExprPredicate& expr) { + const auto pred = static_cast(expr.predicate); + if (pred != Pred::UnusedIndex && pred != Pred::NeverExecute) { + ir.used_predicates.insert(pred); + } + } + + void operator()(const ExprCondCode& expr) {} + + void operator()(const ExprVar& expr) {} + + void operator()(const ExprBoolean& expr) {} + + void operator()(const ExprGprEqual& expr) { + ir.used_registers.insert(expr.gpr); + } + + void Visit(const Expr& node) { + return std::visit(*this, *node); + } + +private: + ShaderIR& ir; +}; + class ASTDecoder { public: - explicit ASTDecoder(ShaderIR& ir_) : ir(ir_) {} + explicit ASTDecoder(ShaderIR& ir_) : ir(ir_), decoder(ir_) {} void operator()(ASTProgram& ast) { ASTNode current = ast.nodes.GetFirst(); @@ -77,6 +120,7 @@ public: } void operator()(ASTIfThen& ast) { + decoder.Visit(ast.condition); ASTNode current = ast.nodes.GetFirst(); while (current) { Visit(current); @@ -96,13 +140,18 @@ public: void operator()(ASTBlockDecoded& ast) {} - void operator()(ASTVarSet& ast) {} + void operator()(ASTVarSet& ast) { + decoder.Visit(ast.condition); + } void operator()(ASTLabel& ast) {} - void operator()(ASTGoto& ast) {} + void operator()(ASTGoto& ast) { + decoder.Visit(ast.condition); + } void operator()(ASTDoWhile& ast) { + decoder.Visit(ast.condition); ASTNode current = ast.nodes.GetFirst(); while (current) { Visit(current); @@ -110,9 +159,13 @@ public: } } - void operator()(ASTReturn& ast) {} + void operator()(ASTReturn& ast) { + decoder.Visit(ast.condition); + } - void operator()(ASTBreak& ast) {} + void operator()(ASTBreak& ast) { + decoder.Visit(ast.condition); + } void Visit(ASTNode& node) { std::visit(*this, *node->GetInnerData()); @@ -125,77 +178,113 @@ public: private: ShaderIR& ir; + ExprDecoder decoder; }; void ShaderIR::Decode() { + const auto decode_function = ([this](ShaderFunction& shader_info) { + coverage_end = std::max(0, shader_info.end); + switch (shader_info.settings.depth) { + case CompileDepth::FlowStack: { + for (const auto& block : shader_info.blocks) { + basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); + } + break; + } + case CompileDepth::NoFlowStack: { + disable_flow_stack = true; + const auto insert_block = [this](NodeBlock& nodes, u32 label) { + if (label == static_cast(exit_branch)) { + return; + } + basic_blocks.insert({label, nodes}); + }; + const auto& blocks = shader_info.blocks; + NodeBlock current_block; + u32 current_label = static_cast(exit_branch); + for (const auto& block : blocks) { + if (shader_info.labels.contains(block.start)) { + insert_block(current_block, current_label); + current_block.clear(); + current_label = block.start; + } + if (!block.ignore_branch) { + DecodeRangeInner(current_block, block.start, block.end); + InsertControlFlow(current_block, block); + } else { + DecodeRangeInner(current_block, block.start, block.end + 1); + } + } + insert_block(current_block, current_label); + break; + } + case CompileDepth::DecompileBackwards: + case CompileDepth::FullDecompile: { + program_manager = std::move(shader_info.manager); + disable_flow_stack = true; + decompiled = true; + ASTDecoder decoder{*this}; + ASTNode program = program_manager.GetProgram(); + decoder.Visit(program); + break; + } + default: + LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); + [[fallthrough]]; + case CompileDepth::BruteForce: { + const auto shader_end = static_cast(program_code.size()); + coverage_begin = main_offset; + coverage_end = shader_end; + for (u32 label = main_offset; label < shader_end; ++label) { + basic_blocks.insert({label, DecodeRange(label, label + 1)}); + } + break; + } + } + if (settings.depth != shader_info.settings.depth) { + LOG_WARNING( + HW_GPU, + "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", + CompileDepthAsString(settings.depth), + CompileDepthAsString(shader_info.settings.depth)); + } + }); + const auto gen_function = + ([this](ShaderFunction& shader_info, u32 id) -> std::shared_ptr { + std::shared_ptr result; + if (decompiled) { + result = std::make_shared(std::move(program_manager), id, + shader_info.start, shader_info.end); + } else { + result = + std::make_shared(std::move(basic_blocks), disable_flow_stack, + id, shader_info.start, shader_info.end); + } + decompiled = false; + disable_flow_stack = false; + basic_blocks.clear(); + program_manager.Clear(); + return result; + }); std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); decompiled = false; auto info = ScanFlow(program_code, main_offset, settings, registry); - auto& shader_info = *info; - coverage_begin = shader_info.start; - coverage_end = shader_info.end; - switch (shader_info.settings.depth) { - case CompileDepth::FlowStack: { - for (const auto& block : shader_info.blocks) { - basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); - } - break; + u32 id_start = 1; + for (auto& pair : info->subfunctions) { + func_map.emplace(pair.first, id_start); + id_start++; } - case CompileDepth::NoFlowStack: { - disable_flow_stack = true; - const auto insert_block = [this](NodeBlock& nodes, u32 label) { - if (label == static_cast(exit_branch)) { - return; - } - basic_blocks.insert({label, nodes}); - }; - const auto& blocks = shader_info.blocks; - NodeBlock current_block; - u32 current_label = static_cast(exit_branch); - for (const auto& block : blocks) { - if (shader_info.labels.contains(block.start)) { - insert_block(current_block, current_label); - current_block.clear(); - current_label = block.start; - } - if (!block.ignore_branch) { - DecodeRangeInner(current_block, block.start, block.end); - InsertControlFlow(current_block, block); - } else { - DecodeRangeInner(current_block, block.start, block.end + 1); - } - } - insert_block(current_block, current_label); - break; - } - case CompileDepth::DecompileBackwards: - case CompileDepth::FullDecompile: { - program_manager = std::move(shader_info.manager); - disable_flow_stack = true; - decompiled = true; - ASTDecoder decoder{*this}; - ASTNode program = GetASTProgram(); - decoder.Visit(program); - break; - } - default: - LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); - [[fallthrough]]; - case CompileDepth::BruteForce: { - const auto shader_end = static_cast(program_code.size()); - coverage_begin = main_offset; - coverage_end = shader_end; - for (u32 label = main_offset; label < shader_end; ++label) { - basic_blocks.insert({label, DecodeRange(label, label + 1)}); - } - break; - } - } - if (settings.depth != shader_info.settings.depth) { - LOG_WARNING( - HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", - CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth)); + coverage_begin = info->main.start; + coverage_end = 0; + decode_function(info->main); + main_function = gen_function(info->main, 0); + subfunctions.resize(info->subfunctions.size()); + for (auto& pair : info->subfunctions) { + auto& func_info = pair.second; + decode_function(func_info); + u32 id = func_map[pair.first]; + subfunctions[id - 1] = gen_function(func_info, id); } } diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 5f88537bc..2bc596512 100755 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -33,6 +33,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { // With the previous preconditions, this instruction is a no-operation. break; } + case OpCode::Id::RET: case OpCode::Id::EXIT: { const ConditionCode cc = instr.flow_condition_code; UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc); @@ -312,6 +313,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed"); break; } + case OpCode::Id::CAL: { + const u32 target = pc + instr.bra.GetBranchTarget(); + const auto it = func_map.find(target); + if (it == func_map.end()) { + UNREACHABLE(); + break; + } + bb.push_back(FunctionCall(it->second)); + break; + } default: UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index c69681e8d..43a166b6f 100755 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -339,8 +339,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { const TextureType texture_type{instr.tlds.GetTextureType()}; const bool is_array{instr.tlds.IsArrayTexture()}; - UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI), - "AOFFI is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented"); const Node4 components = GetTldsCode(instr, texture_type, is_array); @@ -822,7 +820,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is for (std::size_t i = 0; i < type_coord_count; ++i) { const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1); coords.push_back( - GetRegister(last && !aoffi_enabled ? last_coord_register : coord_register + i)); + GetRegister(last && !aoffi_enabled ? last_coord_register : (coord_register + i))); } const Node array = is_array ? GetRegister(array_register) : nullptr; diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index b54d33763..a58e7c65e 100755 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -267,10 +267,11 @@ class PatchNode; class SmemNode; class GmemNode; class CommentNode; +class FunctionCallNode; using NodeData = std::variant; + LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>; using Node = std::shared_ptr; using Node4 = std::array; using NodeBlock = std::vector; @@ -494,6 +495,18 @@ private: std::vector code; ///< Code to execute }; +class FunctionCallNode final : public AmendNode { +public: + explicit FunctionCallNode(u32 func_id_) : func_id{func_id_} {} + + [[nodiscard]] u32 GetFuncId() const { + return func_id; + } + +private: + u32 func_id; ///< Id of the function to call +}; + /// A general purpose register class GprNode final { public: diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp index 6a5b6940d..cef9c26bc 100755 --- a/src/video_core/shader/node_helper.cpp +++ b/src/video_core/shader/node_helper.cpp @@ -19,6 +19,11 @@ Node Comment(std::string text) { return MakeNode(std::move(text)); } +/// Creates a function call +Node FunctionCall(u32 func_id) { + return MakeNode(func_id); +} + Node Immediate(u32 value) { return MakeNode(value); } diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h index 1e0886185..3f882cd25 100755 --- a/src/video_core/shader/node_helper.h +++ b/src/video_core/shader/node_helper.h @@ -27,6 +27,9 @@ Node Conditional(Node condition, std::vector code); /// Creates a commentary node Node Comment(std::string text); +/// Creates a function call +Node FunctionCall(u32 func_id); + /// Creates an u32 immediate Node Immediate(u32 value); diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 1cd7c14d7..94715b069 100755 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -26,7 +26,7 @@ namespace VideoCommon::Shader { struct ShaderBlock; -constexpr u32 MAX_PROGRAM_LENGTH = 0x1000; +constexpr u32 MAX_PROGRAM_LENGTH = 0x2000; struct ConstBuffer { constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_) @@ -64,16 +64,68 @@ struct GlobalMemoryUsage { bool is_written{}; }; +class ShaderFunctionIR final { +public: + explicit ShaderFunctionIR(std::map&& basic_blocks_, bool disable_flow_stack_, + u32 id_, u32 coverage_begin_, u32 coverage_end_) + : basic_blocks{std::move(basic_blocks_)}, decompiled{false}, + disable_flow_stack{disable_flow_stack_}, id{id_}, coverage_begin{coverage_begin_}, + coverage_end{coverage_end_} {} + explicit ShaderFunctionIR(ASTManager&& program_manager_, u32 id_, u32 coverage_begin_, + u32 coverage_end_) + : program_manager{std::move(program_manager_)}, decompiled{true}, disable_flow_stack{true}, + id{id_}, coverage_begin{coverage_begin_}, coverage_end{coverage_end_} {} + + const std::map& GetBasicBlocks() const { + return basic_blocks; + } + + [[nodiscard]] bool IsFlowStackDisabled() const { + return disable_flow_stack; + } + + [[nodiscard]] bool IsDecompiled() const { + return decompiled; + } + + const ASTManager& GetASTManager() const { + return program_manager; + } + + [[nodiscard]] ASTNode GetASTProgram() const { + return program_manager.GetProgram(); + } + + [[nodiscard]] u32 GetASTNumVariables() const { + return program_manager.GetVariables(); + } + + [[nodiscard]] bool IsMain() const { + return id == 0; + } + + [[nodiscard]] u32 GetId() const { + return id; + } + +private: + std::map basic_blocks; + ASTManager program_manager{true, true}; + + bool decompiled{}; + bool disable_flow_stack{}; + u32 id{}; + + u32 coverage_begin{}; + u32 coverage_end{}; +}; + class ShaderIR final { public: explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_, CompilerSettings settings_, Registry& registry_); ~ShaderIR(); - const std::map& GetBasicBlocks() const { - return basic_blocks; - } - const std::set& GetRegisters() const { return used_registers; } @@ -155,26 +207,6 @@ public: return header; } - bool IsFlowStackDisabled() const { - return disable_flow_stack; - } - - bool IsDecompiled() const { - return decompiled; - } - - const ASTManager& GetASTManager() const { - return program_manager; - } - - ASTNode GetASTProgram() const { - return program_manager.GetProgram(); - } - - u32 GetASTNumVariables() const { - return program_manager.GetVariables(); - } - u32 ConvertAddressToNvidiaSpace(u32 address) const { return (address - main_offset) * static_cast(sizeof(Tegra::Shader::Instruction)); } @@ -190,7 +222,16 @@ public: return num_custom_variables; } + std::shared_ptr GetMainFunction() const { + return main_function; + } + + const std::vector>& GetSubFunctions() const { + return subfunctions; + } + private: + friend class ExprDecoder; friend class ASTDecoder; struct SamplerInfo { @@ -453,6 +494,10 @@ private: std::vector amend_code; u32 num_custom_variables{}; + std::shared_ptr main_function; + std::vector> subfunctions; + std::unordered_map func_map; + std::set used_registers; std::set used_predicates; std::set used_input_attributes; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b025ced1c..cc0790e07 100755 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(yuzu applets/profile_select.h applets/software_keyboard.cpp applets/software_keyboard.h + applets/software_keyboard.ui applets/web_browser.cpp applets/web_browser.h bootmanager.cpp @@ -143,6 +144,9 @@ add_executable(yuzu uisettings.h util/limitable_input_dialog.cpp util/limitable_input_dialog.h + util/overlay_dialog.cpp + util/overlay_dialog.h + util/overlay_dialog.ui util/sequence_dialog/sequence_dialog.cpp util/sequence_dialog/sequence_dialog.h util/url_request_interceptor.cpp diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 8ee03ddb3..085688cd4 100755 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/error.cpp @@ -19,11 +19,11 @@ QtErrorDisplay::~QtErrorDisplay() = default; void QtErrorDisplay::ShowError(ResultCode error, std::function finished) const { callback = std::move(finished); emit MainWindowDisplayError( - tr("An error has occurred.\nPlease try again or contact the developer of the " - "software.\n\nError Code: %1-%2 (0x%3)") + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error has occurred.\nPlease try again or contact the developer of the software.")); } void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, @@ -32,13 +32,14 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); emit MainWindowDisplayError( - tr("An error occurred on %1 at %2.\nPlease try again or contact the " - "developer of the software.\n\nError Code: %3-%4 (0x%5)") - .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) - .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the " + "software.") + .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) + .arg(date_time.toString(QStringLiteral("h:mm:ss A")))); } void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text, @@ -46,10 +47,11 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te std::function finished) const { callback = std::move(finished); emit MainWindowDisplayError( - tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") + tr("Error Code: %1-%2 (0x%3)") .arg(static_cast(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) - .arg(error.raw, 8, 16, QChar::fromLatin1('0')) + .arg(error.raw, 8, 16, QChar::fromLatin1('0')), + tr("An error has occurred.\n\n%1\n\n%2") .arg(QString::fromStdString(dialog_text)) .arg(QString::fromStdString(fullscreen_text))); } diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/error.h index b0932d895..8bd895a32 100755 --- a/src/yuzu/applets/error.h +++ b/src/yuzu/applets/error.h @@ -24,7 +24,7 @@ public: std::function finished) const override; signals: - void MainWindowDisplayError(QString error) const; + void MainWindowDisplayError(QString error_code, QString error_text) const; private: void MainWindowFinishedError(); diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index ab8cfd8ee..06bab08d4 100755 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -1,153 +1,1641 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include -#include -#include -#include -#include -#include -#include "core/hle/lock.h" +#include +#include +#include + +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/input_interpreter.h" +#include "core/settings.h" +#include "ui_software_keyboard.h" #include "yuzu/applets/software_keyboard.h" #include "yuzu/main.h" +#include "yuzu/util/overlay_dialog.h" -QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( - Core::Frontend::SoftwareKeyboardParameters parameters) - : parameters(std::move(parameters)) {} +namespace { -QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { - if (input.size() > static_cast(parameters.max_length)) { - return Invalid; - } - if (parameters.disable_space && input.contains(QLatin1Char{' '})) { - return Invalid; - } - if (parameters.disable_address && input.contains(QLatin1Char{'@'})) { - return Invalid; - } - if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) { - return Invalid; - } - if (parameters.disable_slash && - (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) { - return Invalid; - } - if (parameters.disable_number && - std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { - return Invalid; - } +using namespace Service::AM::Applets; - if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) { - return c == QLatin1Char{'O'} || c == QLatin1Char{'I'}; - })) { - return Invalid; - } +constexpr float BASE_HEADER_FONT_SIZE = 23.0f; +constexpr float BASE_SUB_FONT_SIZE = 17.0f; +constexpr float BASE_EDITOR_FONT_SIZE = 26.0f; +constexpr float BASE_CHAR_BUTTON_FONT_SIZE = 28.0f; +constexpr float BASE_LABEL_BUTTON_FONT_SIZE = 18.0f; +constexpr float BASE_ICON_BUTTON_SIZE = 36.0f; +[[maybe_unused]] constexpr float BASE_WIDTH = 1280.0f; +constexpr float BASE_HEIGHT = 720.0f; - return Acceptable; -} +} // Anonymous namespace QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( - QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) - : QDialog(parent), parameters(std::move(parameters_)) { - layout = new QVBoxLayout; + QWidget* parent, Core::System& system_, bool is_inline_, + Core::Frontend::KeyboardInitializeParameters initialize_parameters_) + : QDialog(parent), ui{std::make_unique()}, system{system_}, + is_inline{is_inline_}, initialize_parameters{std::move(initialize_parameters_)} { + ui->setupUi(this); - header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); - header_label->setFont({header_label->font().family(), 11, QFont::Bold}); - if (header_label->text().isEmpty()) - header_label->setText(tr("Enter text:")); + setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint); + setWindowModality(Qt::WindowModal); + setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_TranslucentBackground); - sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); - sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), - sub_label->font().weight(), true}); - sub_label->setHidden(parameters.sub_text.empty()); + keyboard_buttons = {{ + {{ + { + ui->button_1, + ui->button_2, + ui->button_3, + ui->button_4, + ui->button_5, + ui->button_6, + ui->button_7, + ui->button_8, + ui->button_9, + ui->button_0, + ui->button_minus, + ui->button_backspace, + }, + { + ui->button_q, + ui->button_w, + ui->button_e, + ui->button_r, + ui->button_t, + ui->button_y, + ui->button_u, + ui->button_i, + ui->button_o, + ui->button_p, + ui->button_slash, + ui->button_return, + }, + { + ui->button_a, + ui->button_s, + ui->button_d, + ui->button_f, + ui->button_g, + ui->button_h, + ui->button_j, + ui->button_k, + ui->button_l, + ui->button_colon, + ui->button_apostrophe, + ui->button_return, + }, + { + ui->button_z, + ui->button_x, + ui->button_c, + ui->button_v, + ui->button_b, + ui->button_n, + ui->button_m, + ui->button_comma, + ui->button_dot, + ui->button_question, + ui->button_exclamation, + ui->button_ok, + }, + { + ui->button_shift, + ui->button_shift, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_space, + ui->button_ok, + }, + }}, + {{ + { + ui->button_hash, + ui->button_left_bracket, + ui->button_right_bracket, + ui->button_dollar, + ui->button_percent, + ui->button_circumflex, + ui->button_ampersand, + ui->button_asterisk, + ui->button_left_parenthesis, + ui->button_right_parenthesis, + ui->button_underscore, + ui->button_backspace_shift, + }, + { + ui->button_q_shift, + ui->button_w_shift, + ui->button_e_shift, + ui->button_r_shift, + ui->button_t_shift, + ui->button_y_shift, + ui->button_u_shift, + ui->button_i_shift, + ui->button_o_shift, + ui->button_p_shift, + ui->button_at, + ui->button_return_shift, + }, + { + ui->button_a_shift, + ui->button_s_shift, + ui->button_d_shift, + ui->button_f_shift, + ui->button_g_shift, + ui->button_h_shift, + ui->button_j_shift, + ui->button_k_shift, + ui->button_l_shift, + ui->button_semicolon, + ui->button_quotation, + ui->button_return_shift, + }, + { + ui->button_z_shift, + ui->button_x_shift, + ui->button_c_shift, + ui->button_v_shift, + ui->button_b_shift, + ui->button_n_shift, + ui->button_m_shift, + ui->button_less_than, + ui->button_greater_than, + ui->button_plus, + ui->button_equal, + ui->button_ok_shift, + }, + { + ui->button_shift_shift, + ui->button_shift_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_space_shift, + ui->button_ok_shift, + }, + }}, + }}; - guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); - guide_label->setHidden(parameters.guide_text.empty()); + numberpad_buttons = {{ + { + ui->button_1_num, + ui->button_2_num, + ui->button_3_num, + ui->button_backspace_num, + }, + { + ui->button_4_num, + ui->button_5_num, + ui->button_6_num, + ui->button_ok_num, + }, + { + ui->button_7_num, + ui->button_8_num, + ui->button_9_num, + ui->button_ok_num, + }, + { + nullptr, + ui->button_0_num, + nullptr, + ui->button_ok_num, + }, + }}; - length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); - length_label->setAlignment(Qt::AlignRight); - length_label->setFont({length_label->font().family(), 8}); + all_buttons = { + ui->button_1, + ui->button_2, + ui->button_3, + ui->button_4, + ui->button_5, + ui->button_6, + ui->button_7, + ui->button_8, + ui->button_9, + ui->button_0, + ui->button_minus, + ui->button_backspace, + ui->button_q, + ui->button_w, + ui->button_e, + ui->button_r, + ui->button_t, + ui->button_y, + ui->button_u, + ui->button_i, + ui->button_o, + ui->button_p, + ui->button_slash, + ui->button_return, + ui->button_a, + ui->button_s, + ui->button_d, + ui->button_f, + ui->button_g, + ui->button_h, + ui->button_j, + ui->button_k, + ui->button_l, + ui->button_colon, + ui->button_apostrophe, + ui->button_z, + ui->button_x, + ui->button_c, + ui->button_v, + ui->button_b, + ui->button_n, + ui->button_m, + ui->button_comma, + ui->button_dot, + ui->button_question, + ui->button_exclamation, + ui->button_ok, + ui->button_shift, + ui->button_space, + ui->button_hash, + ui->button_left_bracket, + ui->button_right_bracket, + ui->button_dollar, + ui->button_percent, + ui->button_circumflex, + ui->button_ampersand, + ui->button_asterisk, + ui->button_left_parenthesis, + ui->button_right_parenthesis, + ui->button_underscore, + ui->button_backspace_shift, + ui->button_q_shift, + ui->button_w_shift, + ui->button_e_shift, + ui->button_r_shift, + ui->button_t_shift, + ui->button_y_shift, + ui->button_u_shift, + ui->button_i_shift, + ui->button_o_shift, + ui->button_p_shift, + ui->button_at, + ui->button_return_shift, + ui->button_a_shift, + ui->button_s_shift, + ui->button_d_shift, + ui->button_f_shift, + ui->button_g_shift, + ui->button_h_shift, + ui->button_j_shift, + ui->button_k_shift, + ui->button_l_shift, + ui->button_semicolon, + ui->button_quotation, + ui->button_z_shift, + ui->button_x_shift, + ui->button_c_shift, + ui->button_v_shift, + ui->button_b_shift, + ui->button_n_shift, + ui->button_m_shift, + ui->button_less_than, + ui->button_greater_than, + ui->button_plus, + ui->button_equal, + ui->button_ok_shift, + ui->button_shift_shift, + ui->button_space_shift, + ui->button_1_num, + ui->button_2_num, + ui->button_3_num, + ui->button_backspace_num, + ui->button_4_num, + ui->button_5_num, + ui->button_6_num, + ui->button_ok_num, + ui->button_7_num, + ui->button_8_num, + ui->button_9_num, + ui->button_0_num, + }; - line_edit = new QLineEdit; - line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); - line_edit->setMaxLength(static_cast(parameters.max_length)); - line_edit->setText(QString::fromStdU16String(parameters.initial_text)); - line_edit->setCursorPosition( - parameters.cursor_at_beginning ? 0 : static_cast(parameters.initial_text.size())); - line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); + SetupMouseHover(); - connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { - length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); - }); - - buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); - if (parameters.submit_text.empty()) { - buttons->addButton(QDialogButtonBox::Ok); - } else { - buttons->addButton(QString::fromStdU16String(parameters.submit_text), - QDialogButtonBox::AcceptRole); + if (!initialize_parameters.ok_text.empty()) { + ui->button_ok->setText(QString::fromStdU16String(initialize_parameters.ok_text)); + } + + ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text)); + ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text)); + + current_text = initialize_parameters.initial_text; + cursor_position = initialize_parameters.initial_cursor_position; + + SetTextDrawType(); + + for (auto* button : all_buttons) { + connect(button, &QPushButton::clicked, this, [this, button](bool) { + if (is_inline) { + InlineKeyboardButtonClicked(button); + } else { + NormalKeyboardButtonClicked(button); + } + }); + } + + // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend + if (system.IsPoweredOn()) { + input_interpreter = std::make_unique(system); } - connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); - layout->addWidget(header_label); - layout->addWidget(sub_label); - layout->addWidget(guide_label); - layout->addWidget(length_label); - layout->addWidget(line_edit); - layout->addWidget(buttons); - setLayout(layout); - setWindowTitle(tr("Software Keyboard")); } -QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; +QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() { + StopInputThread(); +} -void QtSoftwareKeyboardDialog::accept() { - text = line_edit->text().toStdU16String(); - QDialog::accept(); +void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) { + if (isVisible()) { + return; + } + + MoveAndResizeWindow(pos, size); + + SetKeyboardType(); + SetPasswordMode(); + SetControllerImage(); + DisableKeyboardButtons(); + SetBackspaceOkEnabled(); + + open(); +} + +void QtSoftwareKeyboardDialog::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + case SwkbdTextCheckResult::Silent: + default: + break; + case SwkbdTextCheckResult::Failure: { + StopInputThread(); + + OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message), + QString{}, tr("OK"), Qt::AlignCenter); + dialog.exec(); + + StartInputThread(); + break; + } + case SwkbdTextCheckResult::Confirm: { + StopInputThread(); + + OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message), + tr("Cancel"), tr("OK"), Qt::AlignCenter); + if (dialog.exec() == QDialog::Accepted) { + emit SubmitNormalText(SwkbdResult::Ok, current_text); + break; + } + + StartInputThread(); + break; + } + } +} + +void QtSoftwareKeyboardDialog::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, QSize size) { + MoveAndResizeWindow(pos, size); + + ui->topOSK->setStyleSheet(QStringLiteral("background: rgba(0, 0, 0, 0);")); + + ui->headerOSK->hide(); + ui->subOSK->hide(); + ui->inputOSK->hide(); + ui->charactersOSK->hide(); + ui->inputBoxOSK->hide(); + ui->charactersBoxOSK->hide(); + + initialize_parameters.max_text_length = appear_parameters.max_text_length; + initialize_parameters.min_text_length = appear_parameters.min_text_length; + initialize_parameters.type = appear_parameters.type; + initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; + initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; + initialize_parameters.enable_return_button = appear_parameters.enable_return_button; + initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; + + SetKeyboardType(); + SetControllerImage(); + DisableKeyboardButtons(); + SetBackspaceOkEnabled(); + + open(); +} + +void QtSoftwareKeyboardDialog::HideInlineKeyboard() { + StopInputThread(); + QDialog::hide(); +} + +void QtSoftwareKeyboardDialog::InlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) { + current_text = text_parameters.input_text; + cursor_position = text_parameters.cursor_position; + + SetBackspaceOkEnabled(); +} + +void QtSoftwareKeyboardDialog::ExitKeyboard() { + StopInputThread(); + QDialog::done(QDialog::Accepted); +} + +void QtSoftwareKeyboardDialog::open() { + QDialog::open(); + + row = 0; + column = 0; + + const auto* const curr_button = + keyboard_buttons[static_cast(bottom_osk_index)][row][column]; + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + + StartInputThread(); } void QtSoftwareKeyboardDialog::reject() { - text.clear(); - QDialog::reject(); + // Pressing the ESC key in a dialog calls QDialog::reject(). + // We will override this behavior to the "Cancel" action on the software keyboard. + if (is_inline) { + emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); + } else { + emit SubmitNormalText(SwkbdResult::Cancel, current_text); + } } -std::u16string QtSoftwareKeyboardDialog::GetText() const { - return text; +void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { + if (!is_inline) { + QDialog::keyPressEvent(event); + return; + } + + const auto entered_key = event->key(); + + switch (entered_key) { + case Qt::Key_Escape: + QDialog::keyPressEvent(event); + return; + case Qt::Key_Backspace: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_backspace->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_backspace_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_backspace_num->click(); + break; + default: + break; + } + return; + case Qt::Key_Return: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + return; + case Qt::Key_Left: + MoveTextCursorDirection(Direction::Left); + return; + case Qt::Key_Right: + MoveTextCursorDirection(Direction::Right); + return; + default: + break; + } + + const auto entered_text = event->text(); + + if (entered_text.isEmpty()) { + return; + } + + InlineTextInsertString(entered_text.toStdU16String()); +} + +void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) { + QDialog::move(pos); + QDialog::resize(size); + + // High DPI + const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + + RescaleKeyboardElements(size.width(), size.height(), dpi_scale); +} + +void QtSoftwareKeyboardDialog::RescaleKeyboardElements(float width, float height, float dpi_scale) { + const auto header_font_size = BASE_HEADER_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto sub_font_size = BASE_SUB_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto editor_font_size = BASE_EDITOR_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto char_button_font_size = + BASE_CHAR_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + const auto label_button_font_size = + BASE_LABEL_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; + + QFont header_font(QStringLiteral("MS Shell Dlg 2"), header_font_size, QFont::Normal); + QFont sub_font(QStringLiteral("MS Shell Dlg 2"), sub_font_size, QFont::Normal); + QFont editor_font(QStringLiteral("MS Shell Dlg 2"), editor_font_size, QFont::Normal); + QFont char_button_font(QStringLiteral("MS Shell Dlg 2"), char_button_font_size, QFont::Normal); + QFont label_button_font(QStringLiteral("MS Shell Dlg 2"), label_button_font_size, + QFont::Normal); + + ui->label_header->setFont(header_font); + ui->label_sub->setFont(sub_font); + ui->line_edit_osk->setFont(editor_font); + ui->text_edit_osk->setFont(editor_font); + ui->label_characters->setFont(sub_font); + ui->label_characters_box->setFont(sub_font); + + ui->label_shift->setFont(label_button_font); + ui->label_shift_shift->setFont(label_button_font); + ui->label_cancel->setFont(label_button_font); + ui->label_cancel_shift->setFont(label_button_font); + ui->label_cancel_num->setFont(label_button_font); + ui->label_enter->setFont(label_button_font); + ui->label_enter_shift->setFont(label_button_font); + ui->label_enter_num->setFont(label_button_font); + + for (auto* button : all_buttons) { + if (button == ui->button_return || button == ui->button_return_shift) { + button->setFont(label_button_font); + continue; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + button->setFont(label_button_font); + continue; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + button->setFont(label_button_font); + button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) * + (height / BASE_HEIGHT)); + continue; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + button->setFont(label_button_font); + button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) * + (height / BASE_HEIGHT)); + continue; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || + button == ui->button_ok_num) { + button->setFont(label_button_font); + continue; + } + + button->setFont(char_button_font); + } +} + +void QtSoftwareKeyboardDialog::SetKeyboardType() { + switch (initialize_parameters.type) { + case SwkbdType::Normal: + case SwkbdType::Qwerty: + case SwkbdType::Unknown3: + case SwkbdType::Latin: + case SwkbdType::SimplifiedChinese: + case SwkbdType::TraditionalChinese: + case SwkbdType::Korean: + default: { + bottom_osk_index = BottomOSKIndex::LowerCase; + ui->bottomOSK->setCurrentIndex(static_cast(bottom_osk_index)); + + ui->verticalLayout_2->setStretch(0, 320); + ui->verticalLayout_2->setStretch(1, 400); + + ui->gridLineOSK->setRowStretch(5, 94); + ui->gridBoxOSK->setRowStretch(2, 81); + break; + } + case SwkbdType::NumberPad: { + bottom_osk_index = BottomOSKIndex::NumberPad; + ui->bottomOSK->setCurrentIndex(static_cast(bottom_osk_index)); + + ui->verticalLayout_2->setStretch(0, 370); + ui->verticalLayout_2->setStretch(1, 350); + + ui->gridLineOSK->setRowStretch(5, 144); + ui->gridBoxOSK->setRowStretch(2, 131); + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetPasswordMode() { + switch (initialize_parameters.password_mode) { + case SwkbdPasswordMode::Disabled: + default: + ui->line_edit_osk->setEchoMode(QLineEdit::Normal); + break; + case SwkbdPasswordMode::Enabled: + ui->line_edit_osk->setEchoMode(QLineEdit::Password); + break; + } +} + +void QtSoftwareKeyboardDialog::SetTextDrawType() { + switch (initialize_parameters.text_draw_type) { + case SwkbdTextDrawType::Line: + case SwkbdTextDrawType::DownloadCode: { + ui->topOSK->setCurrentIndex(0); + + if (initialize_parameters.max_text_length <= 10) { + ui->gridLineOSK->setColumnStretch(0, 390); + ui->gridLineOSK->setColumnStretch(1, 500); + ui->gridLineOSK->setColumnStretch(2, 390); + } else { + ui->gridLineOSK->setColumnStretch(0, 130); + ui->gridLineOSK->setColumnStretch(1, 1020); + ui->gridLineOSK->setColumnStretch(2, 130); + } + + if (is_inline) { + return; + } + + connect(ui->line_edit_osk, &QLineEdit::textChanged, [this](const QString& changed_string) { + const auto is_valid = ValidateInputText(changed_string); + + const auto text_length = static_cast(changed_string.length()); + + ui->label_characters->setText(QStringLiteral("%1/%2") + .arg(text_length) + .arg(initialize_parameters.max_text_length)); + + ui->button_ok->setEnabled(is_valid); + ui->button_ok_shift->setEnabled(is_valid); + ui->button_ok_num->setEnabled(is_valid); + + ui->line_edit_osk->setFocus(); + }); + + connect(ui->line_edit_osk, &QLineEdit::cursorPositionChanged, + [this](int old_cursor_position, int new_cursor_position) { + ui->button_backspace->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + ui->button_backspace_shift->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + ui->button_backspace_num->setEnabled( + initialize_parameters.enable_backspace_button && new_cursor_position > 0); + + ui->line_edit_osk->setFocus(); + }); + + connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + }); + + ui->line_edit_osk->setPlaceholderText( + QString::fromStdU16String(initialize_parameters.guide_text)); + ui->line_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text)); + ui->line_edit_osk->setMaxLength(initialize_parameters.max_text_length); + ui->line_edit_osk->setCursorPosition(initialize_parameters.initial_cursor_position); + + ui->label_characters->setText(QStringLiteral("%1/%2") + .arg(initialize_parameters.initial_text.size()) + .arg(initialize_parameters.max_text_length)); + break; + } + case SwkbdTextDrawType::Box: + default: { + ui->topOSK->setCurrentIndex(1); + + if (is_inline) { + return; + } + + connect(ui->text_edit_osk, &QTextEdit::textChanged, [this] { + if (static_cast(ui->text_edit_osk->toPlainText().length()) > + initialize_parameters.max_text_length) { + auto text_cursor = ui->text_edit_osk->textCursor(); + ui->text_edit_osk->setTextCursor(text_cursor); + text_cursor.deletePreviousChar(); + } + + const auto is_valid = ValidateInputText(ui->text_edit_osk->toPlainText()); + + const auto text_length = static_cast(ui->text_edit_osk->toPlainText().length()); + + ui->label_characters_box->setText(QStringLiteral("%1/%2") + .arg(text_length) + .arg(initialize_parameters.max_text_length)); + + ui->button_ok->setEnabled(is_valid); + ui->button_ok_shift->setEnabled(is_valid); + ui->button_ok_num->setEnabled(is_valid); + + ui->text_edit_osk->setFocus(); + }); + + connect(ui->text_edit_osk, &QTextEdit::cursorPositionChanged, [this] { + const auto new_cursor_position = ui->text_edit_osk->textCursor().position(); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + new_cursor_position > 0); + + ui->text_edit_osk->setFocus(); + }); + + ui->text_edit_osk->setPlaceholderText( + QString::fromStdU16String(initialize_parameters.guide_text)); + ui->text_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text)); + ui->text_edit_osk->moveCursor(initialize_parameters.initial_cursor_position == 0 + ? QTextCursor::Start + : QTextCursor::End); + + ui->label_characters_box->setText(QStringLiteral("%1/%2") + .arg(initialize_parameters.initial_text.size()) + .arg(initialize_parameters.max_text_length)); + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetControllerImage() { + const auto controller_type = Settings::values.players.GetValue()[8].connected + ? Settings::values.players.GetValue()[8].controller_type + : Settings::values.players.GetValue()[0].controller_type; + + const QString theme = [] { + if (QIcon::themeName().contains(QStringLiteral("dark")) || + QIcon::themeName().contains(QStringLiteral("midnight"))) { + return QStringLiteral("_dark"); + } else { + return QString{}; + } + }(); + + switch (controller_type) { + case Settings::ControllerType::ProController: + case Settings::ControllerType::GameCube: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); + break; + case Settings::ControllerType::DualJoyconDetached: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); + break; + case Settings::ControllerType::LeftJoycon: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") + .arg(theme)); + break; + case Settings::ControllerType::RightJoycon: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") + .arg(theme)); + break; + case Settings::ControllerType::Handheld: + ui->icon_controller->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + ui->icon_controller_shift->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + ui->icon_controller_num->setStyleSheet( + QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::DisableKeyboardButtons() { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: + default: { + for (const auto& keys : keyboard_buttons) { + for (const auto& rows : keys) { + for (auto* button : rows) { + if (!button) { + continue; + } + + button->setEnabled(true); + } + } + } + + const auto& key_disable_flags = initialize_parameters.key_disable_flags; + + ui->button_space->setDisabled(key_disable_flags.space); + ui->button_space_shift->setDisabled(key_disable_flags.space); + + ui->button_at->setDisabled(key_disable_flags.at || key_disable_flags.username); + + ui->button_percent->setDisabled(key_disable_flags.percent || key_disable_flags.username); + + ui->button_slash->setDisabled(key_disable_flags.slash); + + ui->button_1->setDisabled(key_disable_flags.numbers); + ui->button_2->setDisabled(key_disable_flags.numbers); + ui->button_3->setDisabled(key_disable_flags.numbers); + ui->button_4->setDisabled(key_disable_flags.numbers); + ui->button_5->setDisabled(key_disable_flags.numbers); + ui->button_6->setDisabled(key_disable_flags.numbers); + ui->button_7->setDisabled(key_disable_flags.numbers); + ui->button_8->setDisabled(key_disable_flags.numbers); + ui->button_9->setDisabled(key_disable_flags.numbers); + ui->button_0->setDisabled(key_disable_flags.numbers); + + ui->button_return->setEnabled(initialize_parameters.enable_return_button); + ui->button_return_shift->setEnabled(initialize_parameters.enable_return_button); + break; + } + case BottomOSKIndex::NumberPad: { + for (const auto& rows : numberpad_buttons) { + for (auto* button : rows) { + if (!button) { + continue; + } + + button->setEnabled(true); + } + } + break; + } + } +} + +void QtSoftwareKeyboardDialog::SetBackspaceOkEnabled() { + if (is_inline) { + ui->button_ok->setEnabled(current_text.size() >= initialize_parameters.min_text_length); + ui->button_ok_shift->setEnabled(current_text.size() >= + initialize_parameters.min_text_length); + ui->button_ok_num->setEnabled(current_text.size() >= initialize_parameters.min_text_length); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + cursor_position > 0); + } else { + const auto text_length = [this] { + if (ui->topOSK->currentIndex() == 1) { + return static_cast(ui->text_edit_osk->toPlainText().length()); + } else { + return static_cast(ui->line_edit_osk->text().length()); + } + }(); + + const auto normal_cursor_position = [this] { + if (ui->topOSK->currentIndex() == 1) { + return ui->text_edit_osk->textCursor().position(); + } else { + return ui->line_edit_osk->cursorPosition(); + } + }(); + + ui->button_ok->setEnabled(text_length >= initialize_parameters.min_text_length); + ui->button_ok_shift->setEnabled(text_length >= initialize_parameters.min_text_length); + ui->button_ok_num->setEnabled(text_length >= initialize_parameters.min_text_length); + + ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); + ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); + ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button && + normal_cursor_position > 0); + } +} + +bool QtSoftwareKeyboardDialog::ValidateInputText(const QString& input_text) { + const auto& key_disable_flags = initialize_parameters.key_disable_flags; + + const auto input_text_length = static_cast(input_text.length()); + + if (input_text_length < initialize_parameters.min_text_length || + input_text_length > initialize_parameters.max_text_length) { + return false; + } + + if (key_disable_flags.space && input_text.contains(QLatin1Char{' '})) { + return false; + } + + if ((key_disable_flags.at || key_disable_flags.username) && + input_text.contains(QLatin1Char{'@'})) { + return false; + } + + if ((key_disable_flags.percent || key_disable_flags.username) && + input_text.contains(QLatin1Char{'%'})) { + return false; + } + + if (key_disable_flags.slash && input_text.contains(QLatin1Char{'/'})) { + return false; + } + + if ((key_disable_flags.backslash || key_disable_flags.username) && + input_text.contains(QLatin1Char('\\'))) { + return false; + } + + if (key_disable_flags.numbers && + std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return c.isDigit(); })) { + return false; + } + + if (bottom_osk_index == BottomOSKIndex::NumberPad && + std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return !c.isDigit(); })) { + return false; + } + + return true; +} + +void QtSoftwareKeyboardDialog::ChangeBottomOSKIndex() { + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + bottom_osk_index = BottomOSKIndex::UpperCase; + ui->bottomOSK->setCurrentIndex(static_cast(bottom_osk_index)); + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + break; + case BottomOSKIndex::UpperCase: + if (caps_lock_enabled) { + caps_lock_enabled = false; + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + + ui->label_shift_shift->setText(QStringLiteral("Caps Lock")); + + bottom_osk_index = BottomOSKIndex::LowerCase; + ui->bottomOSK->setCurrentIndex(static_cast(bottom_osk_index)); + } else { + caps_lock_enabled = true; + + ui->button_shift_shift->setStyleSheet( + QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_on.png);" + "\nbackground-position: left top;" + "\nbackground-repeat: no-repeat;" + "\nbackground-origin: content;")); + + ui->button_shift_shift->setIconSize(ui->button_shift->iconSize()); + ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize()); + + ui->label_shift_shift->setText(QStringLiteral("Caps Lock Off")); + } + break; + case BottomOSKIndex::NumberPad: + default: + break; + } +} + +void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) { + if (button == ui->button_ampersand) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral("&")); + } else { + ui->line_edit_osk->insert(QStringLiteral("&")); + } + return; + } + + if (button == ui->button_return || button == ui->button_return_shift) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral("\n")); + } else { + ui->line_edit_osk->insert(QStringLiteral("\n")); + } + return; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(QStringLiteral(" ")); + } else { + ui->line_edit_osk->insert(QStringLiteral(" ")); + } + return; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + ChangeBottomOSKIndex(); + return; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + if (ui->topOSK->currentIndex() == 1) { + auto text_cursor = ui->text_edit_osk->textCursor(); + ui->text_edit_osk->setTextCursor(text_cursor); + text_cursor.deletePreviousChar(); + } else { + ui->line_edit_osk->backspace(); + } + return; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { + if (ui->topOSK->currentIndex() == 1) { + emit SubmitNormalText(SwkbdResult::Ok, + ui->text_edit_osk->toPlainText().toStdU16String()); + } else { + emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String()); + } + return; + } + + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->insertPlainText(button->text()); + } else { + ui->line_edit_osk->insert(button->text()); + } + + // Revert the keyboard to lowercase if the shift key is active. + if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) { + // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase + // if bottom_osk_index is UpperCase and caps_lock_enabled is true. + caps_lock_enabled = true; + ChangeBottomOSKIndex(); + } +} + +void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button) { + if (!button->isEnabled()) { + return; + } + + if (button == ui->button_ampersand) { + InlineTextInsertString(u"&"); + return; + } + + if (button == ui->button_return || button == ui->button_return_shift) { + InlineTextInsertString(u"\n"); + return; + } + + if (button == ui->button_space || button == ui->button_space_shift) { + InlineTextInsertString(u" "); + return; + } + + if (button == ui->button_shift || button == ui->button_shift_shift) { + ChangeBottomOSKIndex(); + return; + } + + if (button == ui->button_backspace || button == ui->button_backspace_shift || + button == ui->button_backspace_num) { + if (cursor_position <= 0 || current_text.empty()) { + cursor_position = 0; + return; + } + + --cursor_position; + + current_text.erase(cursor_position, 1); + + SetBackspaceOkEnabled(); + + emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position); + return; + } + + if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { + emit SubmitInlineText(SwkbdReplyType::DecidedEnter, current_text, cursor_position); + return; + } + + InlineTextInsertString(button->text().toStdU16String()); + + // Revert the keyboard to lowercase if the shift key is active. + if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) { + // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase + // if bottom_osk_index is UpperCase and caps_lock_enabled is true. + caps_lock_enabled = true; + ChangeBottomOSKIndex(); + } +} + +void QtSoftwareKeyboardDialog::InlineTextInsertString(std::u16string_view string) { + if ((current_text.size() + string.size()) > initialize_parameters.max_text_length) { + return; + } + + current_text.insert(cursor_position, string); + + cursor_position += static_cast(string.size()); + + SetBackspaceOkEnabled(); + + emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position); +} + +void QtSoftwareKeyboardDialog::SetupMouseHover() { + // setFocus() has a bug where continuously changing focus will cause the focus UI to + // mysteriously disappear. A workaround we have found is using the mouse to hover over + // the buttons to act in place of the button focus. As a result, we will have to set + // a blank cursor when hovering over all the buttons and set a no focus policy so the + // buttons do not stay in focus in addition to the mouse hover. + for (auto* button : all_buttons) { + button->setCursor(QCursor(Qt::BlankCursor)); + button->setFocusPolicy(Qt::NoFocus); + } +} + +template +void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { + const auto f = [this](HIDButton button) { + if (input_interpreter->IsButtonPressedOnce(button)) { + TranslateButtonPress(button); + } + }; + + (f(T), ...); +} + +template +void QtSoftwareKeyboardDialog::HandleButtonHold() { + const auto f = [this](HIDButton button) { + if (input_interpreter->IsButtonHeld(button)) { + TranslateButtonPress(button); + } + }; + + (f(T), ...); +} + +void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { + switch (button) { + case HIDButton::A: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: + keyboard_buttons[static_cast(bottom_osk_index)][row][column]->click(); + break; + case BottomOSKIndex::NumberPad: + numberpad_buttons[row][column]->click(); + break; + default: + break; + } + break; + case HIDButton::B: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_backspace->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_backspace_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_backspace_num->click(); + break; + default: + break; + } + break; + case HIDButton::X: + if (is_inline) { + emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); + } else { + if (ui->topOSK->currentIndex() == 1) { + emit SubmitNormalText(SwkbdResult::Cancel, + ui->text_edit_osk->toPlainText().toStdU16String()); + } else { + emit SubmitNormalText(SwkbdResult::Cancel, + ui->line_edit_osk->text().toStdU16String()); + } + } + break; + case HIDButton::Y: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_space->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_space_shift->click(); + break; + case BottomOSKIndex::NumberPad: + default: + break; + } + break; + case HIDButton::LStick: + case HIDButton::RStick: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_shift->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_shift_shift->click(); + break; + case BottomOSKIndex::NumberPad: + default: + break; + } + break; + case HIDButton::L: + MoveTextCursorDirection(Direction::Left); + break; + case HIDButton::R: + MoveTextCursorDirection(Direction::Right); + break; + case HIDButton::Plus: + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + ui->button_ok->click(); + break; + case BottomOSKIndex::UpperCase: + ui->button_ok_shift->click(); + break; + case BottomOSKIndex::NumberPad: + ui->button_ok_num->click(); + break; + default: + break; + } + break; + case HIDButton::DLeft: + case HIDButton::LStickLeft: + case HIDButton::RStickLeft: + MoveButtonDirection(Direction::Left); + break; + case HIDButton::DUp: + case HIDButton::LStickUp: + case HIDButton::RStickUp: + MoveButtonDirection(Direction::Up); + break; + case HIDButton::DRight: + case HIDButton::LStickRight: + case HIDButton::RStickRight: + MoveButtonDirection(Direction::Right); + break; + case HIDButton::DDown: + case HIDButton::LStickDown: + case HIDButton::RStickDown: + MoveButtonDirection(Direction::Down); + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) { + // Changes the row or column index depending on the direction. + auto move_direction = [this, direction](std::size_t max_rows, std::size_t max_columns) { + switch (direction) { + case Direction::Left: + column = (column + max_columns - 1) % max_columns; + break; + case Direction::Up: + row = (row + max_rows - 1) % max_rows; + break; + case Direction::Right: + column = (column + 1) % max_columns; + break; + case Direction::Down: + row = (row + 1) % max_rows; + break; + default: + break; + } + }; + + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: { + const auto index = static_cast(bottom_osk_index); + + const auto* const prev_button = keyboard_buttons[index][row][column]; + move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL); + auto* curr_button = keyboard_buttons[index][row][column]; + + while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) { + move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL); + curr_button = keyboard_buttons[index][row][column]; + } + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + case BottomOSKIndex::NumberPad: { + const auto* const prev_button = numberpad_buttons[row][column]; + move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD); + auto* curr_button = numberpad_buttons[row][column]; + + while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) { + move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD); + curr_button = numberpad_buttons[row][column]; + } + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + default: + break; + } +} + +void QtSoftwareKeyboardDialog::MoveTextCursorDirection(Direction direction) { + switch (direction) { + case Direction::Left: + if (is_inline) { + if (cursor_position <= 0) { + cursor_position = 0; + } else { + --cursor_position; + emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position); + } + } else { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->moveCursor(QTextCursor::Left); + } else { + ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() - 1); + } + } + break; + case Direction::Right: + if (is_inline) { + if (cursor_position >= static_cast(current_text.size())) { + cursor_position = static_cast(current_text.size()); + } else { + ++cursor_position; + emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position); + } + } else { + if (ui->topOSK->currentIndex() == 1) { + ui->text_edit_osk->moveCursor(QTextCursor::Right); + } else { + ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() + 1); + } + } + break; + default: + break; + } +} + +void QtSoftwareKeyboardDialog::StartInputThread() { + if (input_thread_running) { + return; + } + + input_thread_running = true; + + input_thread = std::thread(&QtSoftwareKeyboardDialog::InputThread, this); +} + +void QtSoftwareKeyboardDialog::StopInputThread() { + input_thread_running = false; + + if (input_thread.joinable()) { + input_thread.join(); + } + + if (input_interpreter) { + input_interpreter->ResetButtonStates(); + } +} + +void QtSoftwareKeyboardDialog::InputThread() { + while (input_thread_running) { + input_interpreter->PollInput(); + + HandleButtonPressedOnce(); + + HandleButtonHold(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } } QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { - connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, - &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); - connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, - &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); - connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, - &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window, + &GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window, + &GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window, + &GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection); + connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this, + &QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection); + connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this, + &QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection); } QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; -void QtSoftwareKeyboard::RequestText(std::function)> out, - Core::Frontend::SoftwareKeyboardParameters parameters) const { - text_output = std::move(out); - emit MainWindowGetText(parameters); +void QtSoftwareKeyboard::InitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, + std::function submit_normal_callback_, + std::function + submit_inline_callback_) { + if (is_inline) { + submit_inline_callback = std::move(submit_inline_callback_); + } else { + submit_normal_callback = std::move(submit_normal_callback_); + } + + LOG_INFO(Service_AM, + "\nKeyboardInitializeParameters:" + "\nok_text={}" + "\nheader_text={}" + "\nsub_text={}" + "\nguide_text={}" + "\ninitial_text={}" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\ninitial_cursor_position={}" + "\ntype={}" + "\npassword_mode={}" + "\ntext_draw_type={}" + "\nkey_disable_flags={}" + "\nuse_blur_background={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + Common::UTF16ToUTF8(initialize_parameters.ok_text), + Common::UTF16ToUTF8(initialize_parameters.header_text), + Common::UTF16ToUTF8(initialize_parameters.sub_text), + Common::UTF16ToUTF8(initialize_parameters.guide_text), + Common::UTF16ToUTF8(initialize_parameters.initial_text), + initialize_parameters.max_text_length, initialize_parameters.min_text_length, + initialize_parameters.initial_cursor_position, initialize_parameters.type, + initialize_parameters.password_mode, initialize_parameters.text_draw_type, + initialize_parameters.key_disable_flags.raw, initialize_parameters.use_blur_background, + initialize_parameters.enable_backspace_button, + initialize_parameters.enable_return_button, + initialize_parameters.disable_cancel_button); + + emit MainWindowInitializeKeyboard(is_inline, std::move(initialize_parameters)); } -void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, - std::function finished_check_) const { - finished_check = std::move(finished_check_); - emit MainWindowTextCheckDialog(error_message); +void QtSoftwareKeyboard::ShowNormalKeyboard() const { + emit MainWindowShowNormalKeyboard(); } -void QtSoftwareKeyboard::MainWindowFinishedText(std::optional text) { - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - text_output(std::move(text)); +void QtSoftwareKeyboard::ShowTextCheckDialog( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const { + emit MainWindowShowTextCheckDialog(text_check_result, text_check_message); } -void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { - // Acquire the HLE mutex - std::lock_guard lock{HLE::g_hle_lock}; - finished_check(); +void QtSoftwareKeyboard::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const { + LOG_INFO(Service_AM, + "\nInlineAppearParameters:" + "\nmax_text_length={}" + "\nmin_text_length={}" + "\nkey_top_scale_x={}" + "\nkey_top_scale_y={}" + "\nkey_top_translate_x={}" + "\nkey_top_translate_y={}" + "\ntype={}" + "\nkey_disable_flags={}" + "\nkey_top_as_floating={}" + "\nenable_backspace_button={}" + "\nenable_return_button={}" + "\ndisable_cancel_button={}", + appear_parameters.max_text_length, appear_parameters.min_text_length, + appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y, + appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y, + appear_parameters.type, appear_parameters.key_disable_flags.raw, + appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, + appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); + + emit MainWindowShowInlineKeyboard(std::move(appear_parameters)); +} + +void QtSoftwareKeyboard::HideInlineKeyboard() const { + emit MainWindowHideInlineKeyboard(); +} + +void QtSoftwareKeyboard::InlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) const { + LOG_INFO(Service_AM, + "\nInlineTextParameters:" + "\ninput_text={}" + "\ncursor_position={}", + Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); + + emit MainWindowInlineTextChanged(std::move(text_parameters)); +} + +void QtSoftwareKeyboard::ExitKeyboard() const { + emit MainWindowExitKeyboard(); +} + +void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const { + submit_normal_callback(result, submitted_text); +} + +void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, + s32 cursor_position) const { + submit_inline_callback(reply_type, submitted_text, cursor_position); } diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 9e1094cce..1a03c098c 100755 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -1,54 +1,228 @@ -// Copyright 2018 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include +#include +#include +#include + #include #include + #include "core/frontend/applets/software_keyboard.h" +enum class HIDButton : u8; + +class InputInterpreter; + +namespace Core { +class System; +} + +namespace Ui { +class QtSoftwareKeyboardDialog; +} + class GMainWindow; -class QDialogButtonBox; -class QLabel; -class QLineEdit; -class QVBoxLayout; -class QtSoftwareKeyboard; - -class QtSoftwareKeyboardValidator final : public QValidator { -public: - explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters); - State validate(QString& input, int& pos) const override; - -private: - Core::Frontend::SoftwareKeyboardParameters parameters; -}; class QtSoftwareKeyboardDialog final : public QDialog { Q_OBJECT public: - QtSoftwareKeyboardDialog(QWidget* parent, - Core::Frontend::SoftwareKeyboardParameters parameters); + QtSoftwareKeyboardDialog(QWidget* parent, Core::System& system_, bool is_inline_, + Core::Frontend::KeyboardInitializeParameters initialize_parameters_); ~QtSoftwareKeyboardDialog() override; - void accept() override; + void ShowNormalKeyboard(QPoint pos, QSize size); + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, + QSize size); + + void HideInlineKeyboard(); + + void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); + + void ExitKeyboard(); + +signals: + void SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const; + + void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position) const; + +public slots: + void open() override; void reject() override; - std::u16string GetText() const; +protected: + /// We override the keyPressEvent for inputting text into the inline software keyboard. + void keyPressEvent(QKeyEvent* event) override; private: - std::u16string text; + enum class Direction { + Left, + Up, + Right, + Down, + }; - QDialogButtonBox* buttons; - QLabel* header_label; - QLabel* sub_label; - QLabel* guide_label; - QLabel* length_label; - QLineEdit* line_edit; - QVBoxLayout* layout; + enum class BottomOSKIndex { + LowerCase, + UpperCase, + NumberPad, + }; - Core::Frontend::SoftwareKeyboardParameters parameters; + /** + * Moves and resizes the window to a specified position and size. + * + * @param pos Top-left window position + * @param size Window size + */ + void MoveAndResizeWindow(QPoint pos, QSize size); + + /** + * Rescales all keyboard elements to account for High DPI displays. + * + * @param width Window width + * @param height Window height + * @param dpi_scale Display scaling factor + */ + void RescaleKeyboardElements(float width, float height, float dpi_scale); + + /// Sets the keyboard type based on initialize_parameters. + void SetKeyboardType(); + + /// Sets the password mode based on initialize_parameters. + void SetPasswordMode(); + + /// Sets the text draw type based on initialize_parameters. + void SetTextDrawType(); + + /// Sets the controller image at the bottom left of the software keyboard. + void SetControllerImage(); + + /// Disables buttons based on initialize_parameters. + void DisableKeyboardButtons(); + + /// Changes whether the backspace or/and ok buttons should be enabled or disabled. + void SetBackspaceOkEnabled(); + + /** + * Validates the input text sent in based on the parameters in initialize_parameters. + * + * @param input_text Input text + * + * @returns True if the input text is valid, false otherwise. + */ + bool ValidateInputText(const QString& input_text); + + /// Switches between LowerCase and UpperCase (Shift and Caps Lock) + void ChangeBottomOSKIndex(); + + /// Processes a keyboard button click from the UI as normal keyboard input. + void NormalKeyboardButtonClicked(QPushButton* button); + + /// Processes a keyboard button click from the UI as inline keyboard input. + void InlineKeyboardButtonClicked(QPushButton* button); + + /** + * Inserts a string of arbitrary length into the current_text at the current cursor position. + * This is only used for the inline software keyboard. + */ + void InlineTextInsertString(std::u16string_view string); + + /// Setup the mouse hover workaround for "focusing" buttons. This should only be called once. + void SetupMouseHover(); + + /** + * Handles button presses and converts them into keyboard input. + * + * @tparam HIDButton The list of buttons that can be converted into keyboard input. + */ + template + void HandleButtonPressedOnce(); + + /** + * Handles button holds and converts them into keyboard input. + * + * @tparam HIDButton The list of buttons that can be converted into keyboard input. + */ + template + void HandleButtonHold(); + + /** + * Translates a button press to focus or click a keyboard button. + * + * @param button The button press to process. + */ + void TranslateButtonPress(HIDButton button); + + /** + * Moves the focus of a button in a certain direction. + * + * @param direction The direction to move. + */ + void MoveButtonDirection(Direction direction); + + /** + * Moves the text cursor in a certain direction. + * + * @param direction The direction to move. + */ + void MoveTextCursorDirection(Direction direction); + + void StartInputThread(); + void StopInputThread(); + + /// The thread where input is being polled and processed. + void InputThread(); + + std::unique_ptr ui; + + Core::System& system; + + // True if it is the inline software keyboard. + bool is_inline; + + // Common software keyboard initialize parameters. + Core::Frontend::KeyboardInitializeParameters initialize_parameters; + + // Used only by the inline software keyboard since the QLineEdit or QTextEdit is hidden. + std::u16string current_text; + s32 cursor_position{0}; + + static constexpr std::size_t NUM_ROWS_NORMAL = 5; + static constexpr std::size_t NUM_COLUMNS_NORMAL = 12; + static constexpr std::size_t NUM_ROWS_NUMPAD = 4; + static constexpr std::size_t NUM_COLUMNS_NUMPAD = 4; + + // Stores the normal keyboard layout. + std::array, NUM_ROWS_NORMAL>, 2> + keyboard_buttons; + // Stores the numberpad keyboard layout. + std::array, NUM_ROWS_NUMPAD> numberpad_buttons; + + // Contains a set of all buttons used in keyboard_buttons and numberpad_buttons. + std::array all_buttons; + + std::size_t row{0}; + std::size_t column{0}; + + BottomOSKIndex bottom_osk_index{BottomOSKIndex::LowerCase}; + std::atomic caps_lock_enabled{false}; + + std::unique_ptr input_interpreter; + + std::thread input_thread; + + std::atomic input_thread_running{}; }; class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { @@ -58,19 +232,54 @@ public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - void RequestText(std::function)> out, - Core::Frontend::SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message, - std::function finished_check_) const override; + void InitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, + std::function + submit_normal_callback_, + std::function + submit_inline_callback_) override; + + void ShowNormalKeyboard() const override; + + void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const override; + + void ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const override; + + void HideInlineKeyboard() const override; + + void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override; + + void ExitKeyboard() const override; signals: - void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; - void MainWindowTextCheckDialog(std::u16string error_message) const; + void MainWindowInitializeKeyboard( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) const; + + void MainWindowShowNormalKeyboard() const; + + void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const; + + void MainWindowShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) const; + + void MainWindowHideInlineKeyboard() const; + + void MainWindowInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const; + + void MainWindowExitKeyboard() const; private: - void MainWindowFinishedText(std::optional text); - void MainWindowFinishedCheckDialog(); + void SubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text) const; - mutable std::function)> text_output; - mutable std::function finished_check; + void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position) const; + + mutable std::function + submit_normal_callback; + mutable std::function + submit_inline_callback; }; diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp index a93733b26..aad06ac2a 100755 --- a/src/yuzu/discord_impl.cpp +++ b/src/yuzu/discord_impl.cpp @@ -38,7 +38,7 @@ void DiscordImpl::Update() { if (Core::System::GetInstance().IsPoweredOn()) Core::System::GetInstance().GetAppLoader().ReadTitle(title); DiscordRichPresence presence{}; - presence.largeImageKey = "yuzu_logo"; + presence.largeImageKey = "yuzu_logo_ea"; presence.largeImageText = "yuzu is an emulator for the Nintendo Switch"; if (Core::System::GetInstance().IsPoweredOn()) { presence.state = title.c_str(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 23ea4983d..f66b6c476 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -101,6 +101,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/settings.h" #include "core/telemetry_session.h" #include "input_common/main.h" +#include "util/overlay_dialog.h" #include "video_core/gpu.h" #include "video_core/shader_notify.h" #include "yuzu/about_dialog.h" @@ -225,6 +226,8 @@ GMainWindow::GMainWindow() SetDiscordEnabled(UISettings::values.enable_discord_presence); discord_rpc->Update(); + RegisterMetaTypes(); + InitializeWidgets(); InitializeDebugWidgets(); InitializeRecentFileMenuActions(); @@ -373,6 +376,55 @@ GMainWindow::~GMainWindow() { delete render_window; } +void GMainWindow::RegisterMetaTypes() { + // Register integral and floating point types + qRegisterMetaType("u8"); + qRegisterMetaType("u16"); + qRegisterMetaType("u32"); + qRegisterMetaType("u64"); + qRegisterMetaType("u128"); + qRegisterMetaType("s8"); + qRegisterMetaType("s16"); + qRegisterMetaType("s32"); + qRegisterMetaType("s64"); + qRegisterMetaType("f32"); + qRegisterMetaType("f64"); + + // Register string types + qRegisterMetaType("std::string"); + qRegisterMetaType("std::wstring"); + qRegisterMetaType("std::u8string"); + qRegisterMetaType("std::u16string"); + qRegisterMetaType("std::u32string"); + qRegisterMetaType("std::string_view"); + qRegisterMetaType("std::wstring_view"); + qRegisterMetaType("std::u8string_view"); + qRegisterMetaType("std::u16string_view"); + qRegisterMetaType("std::u32string_view"); + + // Register applet types + + // Controller Applet + qRegisterMetaType("Core::Frontend::ControllerParameters"); + + // Software Keyboard Applet + qRegisterMetaType( + "Core::Frontend::KeyboardInitializeParameters"); + qRegisterMetaType( + "Core::Frontend::InlineAppearParameters"); + qRegisterMetaType("Core::Frontend::InlineTextParameters"); + qRegisterMetaType("Service::AM::Applets::SwkbdResult"); + qRegisterMetaType( + "Service::AM::Applets::SwkbdTextCheckResult"); + qRegisterMetaType("Service::AM::Applets::SwkbdReplyType"); + + // Web Browser Applet + qRegisterMetaType("Service::AM::Applets::WebExitReason"); + + // Register loader types + qRegisterMetaType("Core::System::ResultStatus"); +} + void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); @@ -412,25 +464,112 @@ void GMainWindow::ProfileSelectorSelectProfile() { emit ProfileSelectorFinishedSelection(uuid); } -void GMainWindow::SoftwareKeyboardGetText( - const Core::Frontend::SoftwareKeyboardParameters& parameters) { - QtSoftwareKeyboardDialog dialog(this, parameters); - dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | - Qt::WindowTitleHint | Qt::WindowSystemMenuHint | - Qt::WindowCloseButtonHint); - dialog.setWindowModality(Qt::WindowModal); - - if (dialog.exec() == QDialog::Rejected) { - emit SoftwareKeyboardFinishedText(std::nullopt); +void GMainWindow::SoftwareKeyboardInitialize( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) { + if (software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is already initialized!"); return; } - emit SoftwareKeyboardFinishedText(dialog.GetText()); + software_keyboard = new QtSoftwareKeyboardDialog(render_window, Core::System::GetInstance(), + is_inline, std::move(initialize_parameters)); + + if (is_inline) { + connect( + software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this, + [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position); + }, + Qt::QueuedConnection); + } else { + connect( + software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this, + [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text) { + emit SoftwareKeyboardSubmitNormalText(result, submitted_text); + }, + Qt::QueuedConnection); + } } -void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { - QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); - emit SoftwareKeyboardFinishedCheckDialog(); +void GMainWindow::SoftwareKeyboardShowNormal() { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + const auto& layout = render_window->GetFramebufferLayout(); + + const auto x = layout.screen.left; + const auto y = layout.screen.top; + const auto w = layout.screen.GetWidth(); + const auto h = layout.screen.GetHeight(); + + software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); +} + +void GMainWindow::SoftwareKeyboardShowTextCheck( + Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + software_keyboard->ShowTextCheckDialog(text_check_result, text_check_message); +} + +void GMainWindow::SoftwareKeyboardShowInline( + Core::Frontend::InlineAppearParameters appear_parameters) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + const auto& layout = render_window->GetFramebufferLayout(); + + const auto x = + static_cast(layout.screen.left + (0.5f * layout.screen.GetWidth() * + ((2.0f * appear_parameters.key_top_translate_x) + + (1.0f - appear_parameters.key_top_scale_x)))); + const auto y = + static_cast(layout.screen.top + (layout.screen.GetHeight() * + ((2.0f * appear_parameters.key_top_translate_y) + + (1.0f - appear_parameters.key_top_scale_y)))); + const auto w = static_cast(layout.screen.GetWidth() * appear_parameters.key_top_scale_x); + const auto h = static_cast(layout.screen.GetHeight() * appear_parameters.key_top_scale_y); + + software_keyboard->ShowInlineKeyboard(std::move(appear_parameters), + render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); +} + +void GMainWindow::SoftwareKeyboardHideInline() { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + software_keyboard->HideInlineKeyboard(); +} + +void GMainWindow::SoftwareKeyboardInlineTextChanged( + Core::Frontend::InlineTextParameters text_parameters) { + if (!software_keyboard) { + LOG_ERROR(Frontend, "The software keyboard is not initialized!"); + return; + } + + software_keyboard->InlineTextChanged(std::move(text_parameters)); +} + +void GMainWindow::SoftwareKeyboardExit() { + if (!software_keyboard) { + return; + } + + software_keyboard->ExitKeyboard(); + + software_keyboard = nullptr; } void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, @@ -976,6 +1115,10 @@ void GMainWindow::ConnectWidgetEvents() { connect(this, &GMainWindow::EmulationStopping, render_window, &GRenderWindow::OnEmulationStopping); + // Software Keyboard Applet + connect(this, &GMainWindow::EmulationStarting, this, &GMainWindow::SoftwareKeyboardExit); + connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit); + connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar); } @@ -2185,15 +2328,6 @@ void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); - qRegisterMetaType("Core::Frontend::ControllerParameters"); - qRegisterMetaType( - "Core::Frontend::SoftwareKeyboardParameters"); - qRegisterMetaType("Core::System::ResultStatus"); - qRegisterMetaType("std::string"); - qRegisterMetaType>("std::optional"); - qRegisterMetaType("std::string_view"); - qRegisterMetaType("Service::AM::Applets::WebExitReason"); - connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); ui.action_Start->setEnabled(false); @@ -2242,8 +2376,11 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { BootGame(last_filename_booted, program_index); } -void GMainWindow::ErrorDisplayDisplayError(QString body) { - QMessageBox::critical(this, tr("Error Display"), body); +void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { + OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text, + QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); + dialog.exec(); + emit ErrorDisplayFinished(); } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 04d37d4ae..7f1e50a5b 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -37,9 +37,13 @@ enum class GameListRemoveTarget; enum class InstalledEntryType; class GameListPlaceholder; +class QtSoftwareKeyboardDialog; + namespace Core::Frontend { struct ControllerParameters; -struct SoftwareKeyboardParameters; +struct InlineAppearParameters; +struct InlineTextParameters; +struct KeyboardInitializeParameters; } // namespace Core::Frontend namespace DiscordRPC { @@ -57,8 +61,11 @@ class InputSubsystem; } namespace Service::AM::Applets { +enum class SwkbdResult : u32; +enum class SwkbdTextCheckResult : u32; +enum class SwkbdReplyType : u32; enum class WebExitReason : u32; -} +} // namespace Service::AM::Applets enum class EmulatedDirectoryTarget { NAND, @@ -128,8 +135,10 @@ signals: void ProfileSelectorFinishedSelection(std::optional uuid); - void SoftwareKeyboardFinishedText(std::optional text); - void SoftwareKeyboardFinishedCheckDialog(); + void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result, + std::u16string submitted_text); + void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + std::u16string submitted_text, s32 cursor_position); void WebBrowserExtractOfflineRomFS(); void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); @@ -139,15 +148,24 @@ public slots: void OnExecuteProgram(std::size_t program_index); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); - void ErrorDisplayDisplayError(QString body); + void SoftwareKeyboardInitialize( + bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters); + void SoftwareKeyboardShowNormal(); + void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters); + void SoftwareKeyboardHideInline(); + void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); + void SoftwareKeyboardExit(); + void ErrorDisplayDisplayError(QString error_code, QString error_text); void ProfileSelectorSelectProfile(); - void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); - void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, bool is_local); void OnAppFocusStateChanged(Qt::ApplicationState state); private: + void RegisterMetaTypes(); + void InitializeWidgets(); void InitializeDebugWidgets(); void InitializeRecentFileMenuActions(); @@ -334,6 +352,9 @@ private: // Disables the web applet for the rest of the emulated session bool disable_web_applet{}; + // Applets + QtSoftwareKeyboardDialog* software_keyboard = nullptr; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;