summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dist/citra.icnsbin1027012 -> 211056 bytes
-rw-r--r--dist/citra.icobin509287 -> 370070 bytes
-rw-r--r--dist/citra.svg80
-rw-r--r--dist/doc-icon.pngbin8791 -> 7768 bytes
-rw-r--r--src/citra/citra.cpp2
-rw-r--r--src/citra/config.cpp4
-rw-r--r--src/citra/default_ini.h9
-rw-r--r--src/citra_qt/CMakeLists.txt3
-rw-r--r--src/citra_qt/configuration/config.cpp8
-rw-r--r--src/citra_qt/configuration/configure.ui15
-rw-r--r--src/citra_qt/configuration/configure_dialog.cpp1
-rw-r--r--src/citra_qt/configuration/configure_graphics.ui11
-rw-r--r--src/citra_qt/configuration/configure_web.cpp52
-rw-r--r--src/citra_qt/configuration/configure_web.h30
-rw-r--r--src/citra_qt/configuration/configure_web.ui153
-rw-r--r--src/citra_qt/main.cpp46
-rw-r--r--src/citra_qt/main.h2
-rw-r--r--src/citra_qt/ui_settings.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp8
-rw-r--r--src/core/arm/skyeye_common/armstate.h2
-rw-r--r--src/core/file_sys/archive_backend.cpp2
-rw-r--r--src/core/frontend/emu_window.cpp3
-rw-r--r--src/core/frontend/framebuffer_layout.cpp36
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/hle/applets/erreula.cpp4
-rw-r--r--src/core/hle/applets/mii_selector.cpp4
-rw-r--r--src/core/hle/applets/mint.cpp4
-rw-r--r--src/core/hle/applets/swkbd.cpp4
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/core/hle/lock.cpp11
-rw-r--r--src/core/hle/lock.h18
-rw-r--r--src/core/hle/svc.cpp8
-rw-r--r--src/core/hw/gpu.cpp2
-rw-r--r--src/core/hw/gpu.h4
-rw-r--r--src/core/memory.cpp9
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h8
-rw-r--r--src/core/telemetry_session.cpp57
-rw-r--r--src/core/telemetry_session.h12
-rw-r--r--src/input_common/motion_emu.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_state.h3
-rw-r--r--src/video_core/swrasterizer/clipper.cpp4
-rw-r--r--src/video_core/swrasterizer/rasterizer.h6
-rw-r--r--src/web_service/telemetry_json.cpp3
-rw-r--r--src/web_service/telemetry_json.h7
-rw-r--r--src/web_service/web_backend.cpp67
-rw-r--r--src/web_service/web_backend.h18
53 files changed, 609 insertions, 171 deletions
diff --git a/dist/citra.icns b/dist/citra.icns
index 9d3dcca83..ef7bf4e6e 100644
--- a/dist/citra.icns
+++ b/dist/citra.icns
Binary files differ
diff --git a/dist/citra.ico b/dist/citra.ico
index 4fef651e2..2c408b935 100644
--- a/dist/citra.ico
+++ b/dist/citra.ico
Binary files differ
diff --git a/dist/citra.svg b/dist/citra.svg
index 7b299cd89..b6abc1ccf 100644
--- a/dist/citra.svg
+++ b/dist/citra.svg
@@ -1,80 +1,2 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<!-- 2<svg version="1.1" viewBox="0 0 433.93 397.43" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs><linearGradient id="linearGradient7230"><stop stop-color="#f15a24" offset="0"/><stop stop-color="#f15a24" stop-opacity="0" offset="1"/></linearGradient><radialGradient id="radialGradient4297" cx="0" cy="0" r="1" fx=".33908" fy="-.55275" gradientTransform="matrix(135.72 53.102 32.823 -83.888 479.47 287.23)" gradientUnits="userSpaceOnUse"><stop stop-color="#ffff87" offset="0"/><stop stop-color="#ffff87" offset=".12648"/><stop stop-color="#ffc503" offset=".14449"/><stop stop-color="#ffc503" offset=".21612"/><stop stop-color="#ffc503" offset=".39528"/><stop stop-color="#ffc503" offset=".4406"/><stop stop-color="#ffc003" offset=".49983"/><stop stop-color="#ffc003" offset=".62818"/><stop stop-color="#ffc003" offset=".72864"/><stop stop-color="#ff8904" stop-opacity=".86935" offset=".84938"/><stop stop-color="#ff8904" offset=".98742"/><stop stop-color="#ff8904" offset="1"/></radialGradient><radialGradient id="radialGradient4331" cx="-.042447" cy=".042662" r="1" gradientTransform="matrix(97.003 -81.357 -81.357 -97.003 516.33 432.83)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset=".66567"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4351" cx="0" cy="0" r="1" gradientTransform="matrix(-12.353 -91.85 -91.85 12.353 493.83 420.5)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4371" cx="0" cy="0" r="1" fx="-.89202" fy="-.452" gradientTransform="matrix(58.797 44.174 44.174 -58.797 591.5 452.83)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4391" cx="0" cy="0" r="1" gradientTransform="matrix(-63.936 -51.968 -51.968 63.936 480.5 419.5)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4413" cx="-.27681" cy="-.01256" r="1" gradientTransform="matrix(166.21 117.68 75.941 -107.26 545.45 466.23)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffee05" offset=".49699"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4433" cx=".0693" cy=".0013088" r="1" gradientTransform="matrix(92.166 1.56 1.3981 -82.601 484.17 431.17)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4453" cx="0" cy="0" r="1" gradientTransform="matrix(112.47 78.731 51.056 -72.937 527.5 460.5)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffba05" offset="1"/></radialGradient><radialGradient id="radialGradient4475" cx=".13237" cy="-.0012055" r="1" fx="-.080444" fy=".038791" gradientTransform="matrix(64.042 -58.572 -66.76 -72.994 505.91 439.83)" gradientUnits="userSpaceOnUse"><stop stop-color="#ff0" offset="0"/><stop stop-color="#ffcf05" offset=".62977"/><stop stop-color="#ffcf05" offset="1"/></radialGradient><linearGradient id="linearGradient7248-9" x1="382.8" x2="662.93" y1="393.32" y2="393.32" gradientUnits="userSpaceOnUse" xlink:href="#linearGradient7230"/><clipPath id="clipPath4307-2"><path d="m640.25 512.56c0.366-0.686 1.015-1.901 1.507-3.229 0.237-0.588 0.284-0.774 0.375-1.057 0.174-0.52 0.277-0.819 0.368-1.099 0.09-0.28 0.019-0.115 0.107-0.392 0.087-0.277 0.181-0.577 0.264-0.852 0.082-0.276 0.15-0.659 0.224-0.934 0.076-0.276 0.167-0.604 0.233-0.881 0.066-0.276 0.257-1.337 0.312-1.615 0.316-1.599 0.241-2.12 0.401-3.701 0.16-1.58 0.244-3.151 0.26-4.712 0.016-1.563-0.038-3.115-0.154-4.66-0.117-1.545-0.296-3.081-0.533-4.61-0.237-1.528-0.531-3.049-0.877-4.562-0.345-1.513-0.742-3.02-1.184-4.518-0.442-1.5-0.93-2.992-1.457-4.478-0.527-1.485-1.094-2.964-1.694-4.438-1.181-2.896-2.451-5.732-3.805-8.51-1.353-2.778-2.789-5.5-4.302-8.169-1.512-2.671-3.099-5.288-4.755-7.858-1.656-2.571-3.38-5.093-5.165-7.574-1.785-2.48-3.631-4.918-5.53-7.318-1.9-2.4-3.853-4.762-5.852-7.09s-4.044-4.624-6.129-6.89c-2.084-2.267-4.207-4.506-6.362-6.72-2.218-2.28-4.472-4.518-6.76-6.714-2.287-2.197-4.608-4.353-6.961-6.47s-4.737-4.196-7.152-6.237-4.86-4.046-7.334-6.016c-2.473-1.969-4.975-3.904-7.504-5.806-2.528-1.902-5.084-3.771-7.664-5.609-2.581-1.838-5.187-3.645-7.816-5.423-2.63-1.778-5.282-3.527-7.957-5.247-3.325-2.142-6.677-4.217-10.058-6.225-3.381-2.007-6.792-3.946-10.232-5.813-3.441-1.867-6.912-3.661-10.416-5.38s-7.041-3.362-10.611-4.924c-3.571-1.563-7.177-3.046-10.818-4.446-1.846-0.709-3.702-1.398-5.566-2.064-1.814-0.648-3.636-1.274-5.468-1.88-3.716-1.228-7.47-2.37-11.263-3.42-3.794-1.049-7.627-2.009-11.502-2.872-1.624-0.362-3.249-0.695-4.876-1-1.627-0.304-3.255-0.578-4.886-0.823-1.631-0.244-3.262-0.457-4.896-0.638s-3.269-0.33-4.907-0.445c-1.636-0.116-3.275-0.197-4.915-0.245-1.639-0.046-3.28-0.058-4.923-0.034-1.641 0.024-3.285 0.085-4.929 0.183-1.645 0.097-3.29 0.233-4.937 0.408-1.337 0.142-2.664 0.317-3.979 0.531s-2.617 0.466-3.906 0.759c-1.288 0.294-2.562 0.627-3.819 1.007-1.256 0.38-2.496 0.805-3.717 1.278-1.22 0.475-2.422 0.997-3.602 1.571-1.179 0.575-2.337 1.201-3.471 1.884-1.133 0.683-2.244 1.421-3.326 2.22-1.084 0.798-2.656 1.973-3.685 2.893l-3.143-7.259c-3.143-7.26 1.5-6 3.143-8.5 1.644-2.5 0.357-9 0.217-15s6.64-11.5 9.64-14.5c3-3.001 10-10.5 14.5-11.82 4.5-1.319 23.5-16.18 30-18.181 6.5-1.999 13.5-6.999 22-8.499s19-3 27.5-3 26.5-0.5 38.5 0 27 3 33 4 20 2.5 27.5 4.5 23.5 8 27 11.5 28 13.999 33.5 20c5.5 6 24 39.5 25 45.5s14 47.499 17 53c3 5.5 0 39.499 0 45.5 0 6-10 45-10 45l-13.5 28.5-14 14.5z"/></clipPath><radialGradient id="radialGradient7556" cx="698.21" cy="478.94" r="186.38" gradientTransform="matrix(1.2034 -.41433 .38181 1.1089 -338.27 245.75)" gradientUnits="userSpaceOnUse"><stop stop-color="#f15a24" stop-opacity="0" offset="0"/><stop stop-color="#f15a24" stop-opacity="0" offset=".68534"/><stop stop-color="#f14a24" offset="1"/></radialGradient></defs><g transform="translate(-265.81 -357.85)"><g transform="translate(-206,70)"><path d="m651.83 684.53c-17.986-2.258-43.941-8.1382-59.303-13.436-45.416-15.66-91.627-51.387-112.29-86.813-7.3922-12.675-9.8553-29.065-7.6405-50.846 1.5707-15.448 4.2385-24.955 10.938-38.978 20.81-43.561 55.611-85.05 100.64-119.99 33.898-26.298 69.589-47.881 101.64-61.46 27.534-11.666 61.915-21.532 84.068-24.123 27.343-3.1985 56.096 1.1638 74.647 11.325 18.752 10.271 30.61 25.375 43.922 55.939 12.492 28.684 17.312 55.294 17.291 95.461-0.0151 34.075-4.1728 58.552-14.29 84.174-8.402 21.279-21.718 44.014-36.542 62.391-6.6008 8.1824-26.727 28.053-35.45 35-19.832 15.793-45.654 30.387-66.248 37.441-17.156 5.8765-39.99 11.132-59.144 13.612-9.676 1.253-33.307 1.4198-42.243 0.29802z" fill="#fff" stroke-width=".75166"/><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m508.37 418.38c-1.948-9.873-2.175-19.875-2.148-30.734 0.105-4.803-8e-3 -10.436 0.607-16.082 1.248-11.467 5.143-14.109 15.617-9.51 17.862 7.84 33.696 18.829 47.854 32.241 3.988 3.779 8.185 7.422 11.089 12.202 2.673 4.397 2.097 6.88-2.266 9.608-1.396 0.873-2.949 1.54-4.502 2.103-5.181 1.881-10.542 3.121-15.918 4.281-12.621 2.721-25.222 5.644-38.216 5.823-0.08 1e-3 -0.159 1e-3 -0.239 1e-3 -6.452 0-10.63-3.605-11.878-9.933" fill="url(#radialGradient4331)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m492.04 416.52c-3.243-1.617-5.823-4.156-8.361-6.684-16.977-16.905-32.998-34.704-48.672-52.819-4.26-4.924-8.651-9.823-11.376-15.886-1.919-4.27-0.934-6.454 3.654-7.668 4.725-1.249 9.544-1.787 14.436-1.358 11.696 1.026 22.799 4.32 33.655 8.677 10.044 4.032 16.339 11.18 18.544 21.838 2.891 13.979 3.907 28.175 4.618 42.392 0 2.182 0.021 4.364-0.013 6.545-0.01 0.636-0.15 1.274-0.276 1.902-0.545 2.722-1.447 4.01-3.104 4.01-0.83 0-1.848-0.323-3.105-0.949" fill="url(#radialGradient4351)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m623.43 485.96c-2.879-0.515-5.736-1.318-8.503-2.282-15.719-5.482-30.649-12.847-45.782-19.703-12.92-5.855-25.833-11.785-38.083-19.013-2.413-1.425-5.354-3.1-4.765-6.319 0.504-2.751 3.758-2.789 6.016-3.319 17.339-4.073 35.01-5.712 51.316-7.058 10.474 0.119 18.926 1.786 25.516 8.829 6.88 7.354 12.632 15.489 16.919 24.601 2.691 5.72 4.477 11.786 4.479 18.169 2e-3 4.153-1.835 6.272-5.267 6.272-0.571 0-1.188-0.059-1.846-0.177" fill="url(#radialGradient4371)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m475.92 419.25c-0.367-0.104-0.71-0.292-1.065-0.437-22.555-9.166-44.21-20.249-65.865-31.321-5.464-2.792-10.748-5.939-15.684-9.652-5.297-3.984-8.316-8.98-7.651-15.694 0-0.89-0.035-1.662 5e-3 -2.431 0.397-7.627 4.249-12.981 10.812-16.559 6.098-3.323 11.35-1.172 16.365 2.52 1.236 0.911 2.498 1.799 3.649 2.811 22.843 20.104 43.28 42.565 63.347 65.381 1.264 1.437 3.447 3.141 2.012 5.299-0.613 0.922-1.333 1.232-2.102 1.231-1.232 0-2.589-0.797-3.823-1.148" fill="url(#radialGradient4391)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m589.81 520.12c-2.155-1.598-4.18-3.381-6.195-5.159-14.338-12.647-27.085-26.892-40.259-40.697-5.472-5.734-10.87-11.588-15.617-17.981-0.828-1.116-1.856-2.369-0.83-3.824 0.972-1.379 2.519-1.147 3.87-0.737 1.831 0.556 3.62 1.272 5.386 2.018 23.705 10.024 46.647 21.655 69.496 33.464 6.568 3.394 13.185 6.854 18.588 12.131 2.02 1.974 3.277 4.24 3.342 7.436-0.072 2.307-0.943 4.698-2.221 6.96-4.063 7.191-12.308 11.156-20.705 11.156-5.215 0-10.489-1.53-14.855-4.767" fill="url(#radialGradient4413)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m413.44 432.07c-5.861-6.67-11.407-13.524-15.914-21.172-2.874-4.876-5.175-10.064-6.355-15.632-0.819-3.866 0.412-5.458 3.711-5.517 2.078 0.023 4.038 0.607 5.971 1.3 18.942 6.786 36.964 15.693 55.17 24.173 7.774 3.62 15.486 7.375 22.781 11.914 2.187 1.361 4.634 2.997 4.191 5.914-0.396 2.604-3.356 2.319-5.257 3.169-0.692 0.308-1.469 0.436-2.215 0.612-11.727 2.765-23.679 4.039-35.625 5.305-1.381 0.147-2.734 0.221-4.058 0.221-8.707 0-16.182-3.211-22.4-10.287" fill="url(#radialGradient4433)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m567.31 526.82c-14.169-1.46-27.39-6.06-40.199-12.109-7.73-3.652-12.567-9.493-14.146-17.954-1.793-9.597-3.042-19.253-2.904-29.045 0-2.697-0.068-5.395 0.022-8.089 0.067-1.997-0.399-4.391 1.83-5.503 2.219-1.108 4.351 0.084 6.055 1.392 2.834 2.177 5.567 4.529 8.099 7.054 14.061 14.024 27.283 28.841 40.42 43.726 4.051 4.589 8.321 9.1 10.964 14.772 1.594 3.423 0.812 4.978-2.88 5.7-1.112 0.218-2.231 0.3-3.353 0.3-1.302 0-2.607-0.11-3.908-0.244" fill="url(#radialGradient4453)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m493.43 497.7c-3.943-1.236-7.454-3.334-11.002-5.368-14.602-8.364-28.162-18.128-39.782-30.405-5.392-5.696-4.536-9.429 2.935-11.564 13.892-3.97 27.998-7.036 42.519-7.395 7.646-0.189 11.495 3.378 12.86 10.836 1.395 7.615 1.69 15.275 1.485 25.395-0.22 3.072 0.784 8.645-0.687 14.051-0.943 3.472-2.527 5.026-5.134 5.025-0.929 0-1.988-0.197-3.194-0.575" fill="url(#radialGradient4475)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)"><path d="m641.75 509.34c0.237-0.588 0.284-0.774 0.375-1.057 0.174-0.52 0.277-0.819 0.368-1.099 0.09-0.28 0.019-0.115 0.107-0.392 0.087-0.277 0.181-0.577 0.264-0.852 0.082-0.276 0.15-0.659 0.224-0.934 0.076-0.276 0.167-0.604 0.233-0.881 0.066-0.276 0.257-1.337 0.312-1.615 0.316-1.599 0.241-2.12 0.401-3.701 0.16-1.58 0.244-3.151 0.26-4.712 0.016-1.563-0.038-3.115-0.154-4.66-0.117-1.545-0.296-3.081-0.533-4.61-0.237-1.528-0.531-3.049-0.877-4.562-0.345-1.513-0.742-3.02-1.184-4.518-0.442-1.5-0.93-2.992-1.457-4.478-0.527-1.485-1.094-2.964-1.694-4.438-1.181-2.896-2.451-5.732-3.805-8.51-1.353-2.778-2.789-5.5-4.302-8.169-1.512-2.671-3.099-5.288-4.755-7.858-1.656-2.571-3.38-5.093-5.165-7.574-1.785-2.48-3.631-4.918-5.53-7.318-1.9-2.4-3.853-4.762-5.852-7.09s-4.044-4.624-6.129-6.89c-2.084-2.267-4.207-4.506-6.362-6.72-2.218-2.28-4.472-4.518-6.76-6.714-2.287-2.197-4.608-4.353-6.961-6.47s-4.737-4.196-7.152-6.237-4.86-4.046-7.334-6.016c-2.473-1.969-4.975-3.904-7.504-5.806-2.528-1.902-5.084-3.771-7.664-5.609-2.581-1.838-5.187-3.645-7.816-5.423-2.63-1.778-5.282-3.527-7.957-5.247-3.325-2.142-6.677-4.217-10.058-6.225-3.381-2.007-6.792-3.946-10.232-5.813-3.441-1.867-6.912-3.661-10.416-5.38s-7.041-3.362-10.611-4.924c-3.571-1.563-7.177-3.046-10.818-4.446-1.846-0.709-3.702-1.398-5.566-2.064-1.814-0.648-3.636-1.274-5.468-1.88-3.716-1.228-7.47-2.37-11.263-3.42-3.794-1.049-7.627-2.009-11.502-2.872-1.624-0.362-3.249-0.695-4.876-1-1.627-0.304-3.255-0.578-4.886-0.823-1.631-0.244-3.262-0.457-4.896-0.638s-3.269-0.33-4.907-0.445c-1.636-0.116-3.275-0.197-4.915-0.245-1.639-0.046-3.28-0.058-4.923-0.034-1.641 0.024-3.285 0.085-4.929 0.183-1.645 0.097-3.29 0.233-4.937 0.408-1.337 0.142-2.664 0.317-3.979 0.531s-2.617 0.466-3.906 0.759c-1.288 0.294-2.562 0.627-3.819 1.007-1.256 0.38-2.496 0.805-3.717 1.278-1.22 0.475-2.422 0.997-3.602 1.571-0.749 0.365-1.489 0.751-2.22 1.159-0.522 0.286-1.038 0.583-1.55 0.891-1.134 0.683-2.243 1.421-3.327 2.219-0.744 0.549-1.475 1.125-2.194 1.731-0.282 0.225-0.554 0.448-0.806 0.662 5.18-5.701 10.508-11.008 15.987-15.904 5.538-4.95 11.227-9.48 17.07-13.573 5.843-4.094 11.839-7.751 17.987-10.956 6.148-3.204 12.449-5.954 18.901-8.236 6.453-2.28 13.059-4.091 19.816-5.415 6.758-1.325 13.668-2.162 20.731-2.495s14.278-0.163 21.645 0.528c7.368 0.692 14.887 1.904 22.56 3.654 8.186 1.866 15.978 4.164 23.376 6.884 7.397 2.719 14.401 5.861 21.012 9.414 6.611 3.555 12.828 7.522 18.655 11.891 5.826 4.371 11.261 9.143 16.304 14.311 5.045 5.167 9.698 10.729 13.962 16.675 4.264 5.947 8.14 12.278 11.626 18.985 3.487 6.707 6.586 13.789 9.296 21.238 2.712 7.449 5.037 15.264 6.975 23.436 0.548 2.31 1.025 4.624 1.432 6.944 0.408 2.318 0.747 4.641 1.023 6.968 0.275 2.327 0.486 4.658 0.637 6.991 0.15 2.333 0.241 4.671 0.276 7.01 0.034 2.339 0.013 4.681-0.061 7.024-0.074 2.345-0.2 4.69-0.374 7.037-0.174 2.348-0.396 4.697-0.664 7.046-0.266 2.349-0.577 4.699-0.928 7.049-0.39 2.617-0.912 5.345-1.535 8.125-0.623 2.779-1.346 5.609-2.142 8.427-0.795 2.819-1.66 5.626-2.569 8.359-0.907 2.733-1.855 5.391-2.816 7.915-0.959 2.523-1.931 4.909-2.883 7.098-0.953 2.189-1.888 4.179-2.773 5.909-0.886 1.73-1.723 3.199-2.481 4.346-0.475 0.717-1.25 1.831-1.946 2.775 0.215-0.462 0.433-0.967 0.627-1.49" fill="url(#radialGradient4297)"/></g><g transform="matrix(1.3333 0 0 -1.3333 0 1024)" fill="url(#linearGradient7248-9)"><g clip-path="url(#clipPath4307-2)" fill="url(#linearGradient7248-9)"><g transform="translate(382.92 331.6)" fill="url(#linearGradient7248-9)"><path d="m0 0c6.615-7.221 13.678-14.033 21.222-20.267 7.539-6.237 15.577-11.868 24.046-16.747 8.468-4.879 17.397-8.961 26.656-12.07 9.256-3.117 18.837-5.252 28.533-6.374 9.697-1.126 19.503-1.24 29.23-0.453 9.731 0.785 19.379 2.465 28.846 4.853 18.922 4.75 37.215 12.333 53.364 23.285 8.065 5.472 15.569 11.768 22.33 18.787 6.768 7.012 12.789 14.739 18.015 22.967 10.462 16.478 17.733 34.859 22.474 53.789l0.439 1.778 0.209 0.872 0.186 0.899 0.747 3.597 0.373 1.798 0.093 0.45 0.024 0.112c-7e-3 -0.038 7e-3 0.055 0.01 0.076l0.034 0.227 0.134 0.908 0.539 3.634 0.269 1.817c0.077 0.534 0.118 1.199 0.18 1.791l0.338 3.658 0.164 1.796 0.075 1.835 0.15 3.671c0.264 9.752-0.472 19.521-1.882 29.184-1.502 9.646-4.173 19.05-7.353 28.281l-1.228 3.463-0.307 0.865-0.144 0.409-0.173 0.426-0.689 1.702-1.379 3.405-0.685 1.671-0.778 1.664-1.557 3.327-0.371 0.788-0.455 0.798-0.909 1.596-1.8039 3.187c-12.883 30.325-179.5-236.54-259.07-177.56zm-8e-3 8e-3c92.186-40.479 231.35 107.76 257.54 180.96 0.828-0.982 1.523-1.975 2.238-2.986 0.351-0.52 0.707-0.981 1.036-1.569l0.913-1.594 0.912-1.594 0.457-0.797c0.183-0.358 0.275-0.576 0.417-0.871l2.346-4.986c0.271-0.617 0.473-1.148 0.713-1.726l1.387-3.402 0.693-1.701 0.173-0.425 0.164-0.457 0.309-0.865 1.235-3.459c3.22-9.271 5.937-18.782 7.475-28.508 1.444-9.716 2.213-19.555 1.972-29.393l-0.14-3.671-0.069-1.836-0.164-1.863-0.329-3.659c-0.063-0.625-0.09-1.185-0.183-1.864l-0.264-1.818-0.528-3.635-0.132-0.909-0.033-0.227-0.024-0.152-0.023-0.112-0.092-0.45-0.369-1.8-0.736-3.599-0.185-0.9-0.216-0.915-0.436-1.79c-2.367-9.534-5.355-18.932-9.077-28.033-3.717-9.105-8.196-17.911-13.474-26.221-5.276-8.31-11.358-16.118-18.197-23.206-6.832-7.094-14.416-13.459-22.563-18.989-8.15-5.528-16.852-10.229-25.9-14.096-9.054-3.857-18.44-6.901-27.983-9.247-9.54-2.354-19.27-3.997-29.075-4.737-9.804-0.741-19.683-0.576-29.444 0.61-9.761 1.181-19.397 3.381-28.694 6.565-9.3 3.177-18.256 7.329-26.737 12.275-8.488 4.941-16.474 10.71-23.972 17.028-7.497 6.325-14.5 13.22-21.044 20.512z" fill="url(#linearGradient7248-9)"/></g></g></g><path d="m853.83 340.91z" fill="none" stroke="#000" stroke-width="1px"/><path d="m512.59 583.25c24.074 19.047 61.695 18.896 102.99 7.7902 41.299-11.105 86.278-33.164 124.63-62.438 43.462-33.178 74.436-61.06 97.049-98.852 10.211-17.065 16.881-32.679 20.405-47.4 3.5243-14.721 2.1355-27.046-3.4171-40.626 0 0 22.052 31.822 28.025 81.607 2.9864 24.893 1.4477 59.407-11.26 93.337-11.843 31.622-27.743 61.223-54.874 85.55-1.0301 0.92357-12.207 12.478-32.938 24.374-25.596 14.688-46.884 22.5-75.534 27.634-34.926 6.2583-59.469 4.4522-85.833-1.8111-36.815-8.7462-71.794-28.542-110.24-69.041" fill="url(#radialGradient7556)"/></g></g></svg>
3 Copyright 2014 Citra Emulator Project
4 Licensed under GPLv2 or any later version
5 Refer to the license.txt file included.
6-->
7<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 341.071 338.846">
8 <radialGradient id="a" cx="170.5356" cy="167.271" r="170.5332" gradientTransform="matrix(1 0 0 0.9935 0 3.2396)" gradientUnits="userSpaceOnUse">
9 <stop offset="0.5193" stop-color="#FFFFFF" stop-opacity="0.1"/>
10 <stop offset="0.9415" stop-color="#000000" stop-opacity="0.5"/>
11 <stop offset="1" stop-color="#1A1818" stop-opacity="0"/>
12 </radialGradient>
13 <ellipse fill="url(#a)" cx="170.535" cy="169.423" rx="170.535" ry="169.423"/>
14 <circle fill="#D16F17" cx="170.536" cy="167.885" r="161.557"/>
15 <linearGradient id="b" gradientUnits="userSpaceOnUse" x1="234.4458" y1="33.5771" x2="97.5655" y2="321.2358">
16 <stop offset="0" stop-color="#FFF8BD"/>
17 <stop offset="1" stop-color="#F6DCAE"/>
18 </linearGradient>
19 <circle fill="url(#b)" cx="170.536" cy="167.885" r="155.295"/>
20 <g>
21 <linearGradient id="c" gradientUnits="userSpaceOnUse" x1="332.436" y1="91.7446" x2="111.1593" y2="342.0988">
22 <stop offset="0" stop-color="#F7A076"/>
23 <stop offset="0.4455" stop-color="#F3816C"/>
24 <stop offset="1" stop-color="#F06878"/>
25 </linearGradient>
26 <path fill="url(#c)" stroke="#F06564" stroke-miterlimit="10" d="M309.704,123.138
27 c-5.9-7.802-128.517,44.681-128.517,44.681S303.803,221.01,309.704,212.5C322.434,194.142,323.182,140.957,309.704,123.138z"/>
28 <linearGradient id="d" gradientUnits="userSpaceOnUse" x1="285.5845" y1="50.3345" x2="64.3074" y2="300.6891">
29 <stop offset="0" stop-color="#9DC63B"/>
30 <stop offset="1" stop-color="#9BC183"/>
31 </linearGradient>
32 <path fill="url(#d)" stroke="#72AA42" stroke-miterlimit="10" d="M300.518,100.96c-3.98-21.983-41.059-60.12-63.189-63.188
33 c-9.688-1.345-59.28,122.469-59.28,122.469S302.364,111.149,300.518,100.96z"/>
34 <linearGradient id="e" gradientUnits="userSpaceOnUse" x1="229.4995" y1="0.7637" x2="8.2231" y2="251.1176">
35 <stop offset="0" stop-color="#D5DE26"/>
36 <stop offset="1" stop-color="#C5D94B"/>
37 </linearGradient>
38 <path fill="url(#e)" stroke="#BECD30" stroke-miterlimit="10" d="M215.151,28.584c-18.357-12.73-71.543-13.478-89.362,0.001
39 c-7.801,5.899,44.682,128.516,44.682,128.516S223.663,34.484,215.151,28.584z"/>
40 <linearGradient id="f" gradientUnits="userSpaceOnUse" x1="219.3823" y1="-8.1782" x2="-1.8941" y2="242.1756">
41 <stop offset="0" stop-color="#F2D200"/>
42 <stop offset="1" stop-color="#FDEF52"/>
43 </linearGradient>
44 <path fill="url(#f)" stroke="#E1BE29" stroke-miterlimit="10" d="M162.893,160.239c0,0-49.092-124.315-59.281-122.469
45 c-21.982,3.979-60.12,41.058-63.188,63.189C39.078,110.646,162.893,160.239,162.893,160.239z"/>
46 <linearGradient id="g" gradientUnits="userSpaceOnUse" x1="226.0718" y1="-2.2656" x2="4.7951" y2="248.0886">
47 <stop offset="0" stop-color="#FFCD10"/>
48 <stop offset="1" stop-color="#F29634"/>
49 </linearGradient>
50 <path fill="url(#g)" stroke="#F79421" stroke-miterlimit="10" d="M31.236,123.136c-12.73,18.357-13.479,71.543,0,89.362
51 c5.898,7.801,128.516-44.682,128.516-44.682S37.135,114.625,31.236,123.136z"/>
52 <linearGradient id="h" gradientUnits="userSpaceOnUse" x1="272.9214" y1="39.144" x2="51.6446" y2="289.4984">
53 <stop offset="0" stop-color="#F79F1C"/>
54 <stop offset="0.4455" stop-color="#F08021"/>
55 <stop offset="1" stop-color="#ED693C"/>
56 </linearGradient>
57 <path fill="url(#h)" stroke="#F16622" stroke-miterlimit="10" d="M40.422,234.676c3.979,21.982,41.057,60.12,63.188,63.188
58 c9.687,1.346,59.279-122.468,59.279-122.468S38.574,224.487,40.422,234.676z"/>
59 <linearGradient id="i" gradientUnits="userSpaceOnUse" x1="329.0083" y1="88.7129" x2="107.7311" y2="339.0677">
60 <stop offset="0" stop-color="#E47C26"/>
61 <stop offset="0.4455" stop-color="#DF5B27"/>
62 <stop offset="1" stop-color="#DD3A3A"/>
63 </linearGradient>
64 <path fill="url(#i)" stroke="#E03827" stroke-miterlimit="10" d="M125.787,307.051c18.357,12.73,71.543,13.48,89.362,0
65 c7.801-5.898-44.681-128.515-44.681-128.515S117.275,301.153,125.787,307.051z"/>
66 <linearGradient id="j" gradientUnits="userSpaceOnUse" x1="339.1245" y1="97.6562" x2="117.8478" y2="348.0104">
67 <stop offset="0" stop-color="#F3783C"/>
68 <stop offset="0.4455" stop-color="#EF5339"/>
69 <stop offset="1" stop-color="#ED294A"/>
70 </linearGradient>
71 <path fill="url(#j)" stroke="#ED2836" stroke-miterlimit="10" d="M178.047,175.398c0,0,49.09,124.315,59.28,122.467
72 c21.982-3.979,60.121-41.057,63.189-63.188C301.86,224.991,178.047,175.398,178.047,175.398z"/>
73 </g>
74 <linearGradient id="k" gradientUnits="userSpaceOnUse" x1="170.5352" y1="6.3281" x2="170.5351" y2="329.4424">
75 <stop offset="0" stop-color="#FFFFFF" stop-opacity="0.2"/>
76 <stop offset="0.4504" stop-color="#908E8E" stop-opacity="0.05"/>
77 <stop offset="1" stop-color="#030003" stop-opacity="0.2"/>
78 </linearGradient>
79 <circle fill="url(#k)" cx="170.536" cy="167.885" r="161.557"/>
80</svg>
diff --git a/dist/doc-icon.png b/dist/doc-icon.png
index 420b1546f..9b5773214 100644
--- a/dist/doc-icon.png
+++ b/dist/doc-icon.png
Binary files differ
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 14574e56c..e524c5535 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -165,6 +165,8 @@ int main(int argc, char** argv) {
165 break; // Expected case 165 break; // Expected case
166 } 166 }
167 167
168 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
169
168 while (emu_window->IsOpen()) { 170 while (emu_window->IsOpen()) {
169 system.RunLoop(); 171 system.RunLoop();
170 } 172 }
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 73846ed91..3869b6b5d 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -156,8 +156,12 @@ void Config::ReadValues() {
156 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 156 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
157 157
158 // Web Service 158 // Web Service
159 Settings::values.enable_telemetry =
160 sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
159 Settings::values.telemetry_endpoint_url = sdl2_config->Get( 161 Settings::values.telemetry_endpoint_url = sdl2_config->Get(
160 "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); 162 "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
163 Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", "");
164 Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", "");
161} 165}
162 166
163void Config::Reload() { 167void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 9ea779dd8..ea02a788d 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -176,7 +176,14 @@ use_gdbstub=false
176gdbstub_port=24689 176gdbstub_port=24689
177 177
178[WebService] 178[WebService]
179# Whether or not to enable telemetry
180# 0: No, 1 (default): Yes
181enable_telemetry =
179# Endpoint URL for submitting telemetry data 182# Endpoint URL for submitting telemetry data
180telemetry_endpoint_url = 183telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
184# Username and token for Citra Web Service
185# See https://services.citra-emu.org/ for more info
186citra_username =
187citra_token =
181)"; 188)";
182} 189}
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index f364b2284..e0a19fd9e 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -12,6 +12,7 @@ set(SRCS
12 configuration/configure_graphics.cpp 12 configuration/configure_graphics.cpp
13 configuration/configure_input.cpp 13 configuration/configure_input.cpp
14 configuration/configure_system.cpp 14 configuration/configure_system.cpp
15 configuration/configure_web.cpp
15 debugger/graphics/graphics.cpp 16 debugger/graphics/graphics.cpp
16 debugger/graphics/graphics_breakpoint_observer.cpp 17 debugger/graphics/graphics_breakpoint_observer.cpp
17 debugger/graphics/graphics_breakpoints.cpp 18 debugger/graphics/graphics_breakpoints.cpp
@@ -42,6 +43,7 @@ set(HEADERS
42 configuration/configure_graphics.h 43 configuration/configure_graphics.h
43 configuration/configure_input.h 44 configuration/configure_input.h
44 configuration/configure_system.h 45 configuration/configure_system.h
46 configuration/configure_web.h
45 debugger/graphics/graphics.h 47 debugger/graphics/graphics.h
46 debugger/graphics/graphics_breakpoint_observer.h 48 debugger/graphics/graphics_breakpoint_observer.h
47 debugger/graphics/graphics_breakpoints.h 49 debugger/graphics/graphics_breakpoints.h
@@ -71,6 +73,7 @@ set(UIS
71 configuration/configure_graphics.ui 73 configuration/configure_graphics.ui
72 configuration/configure_input.ui 74 configuration/configure_input.ui
73 configuration/configure_system.ui 75 configuration/configure_system.ui
76 configuration/configure_web.ui
74 debugger/registers.ui 77 debugger/registers.ui
75 hotkeys.ui 78 hotkeys.ui
76 main.ui 79 main.ui
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 6e42db007..e2dceaa4c 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -139,10 +139,13 @@ void Config::ReadValues() {
139 qt_config->endGroup(); 139 qt_config->endGroup();
140 140
141 qt_config->beginGroup("WebService"); 141 qt_config->beginGroup("WebService");
142 Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool();
142 Settings::values.telemetry_endpoint_url = 143 Settings::values.telemetry_endpoint_url =
143 qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") 144 qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
144 .toString() 145 .toString()
145 .toStdString(); 146 .toStdString();
147 Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString();
148 Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString();
146 qt_config->endGroup(); 149 qt_config->endGroup();
147 150
148 qt_config->beginGroup("UI"); 151 qt_config->beginGroup("UI");
@@ -194,6 +197,7 @@ void Config::ReadValues() {
194 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 197 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
195 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); 198 UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
196 UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); 199 UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
200 UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
197 201
198 qt_config->endGroup(); 202 qt_config->endGroup();
199} 203}
@@ -283,8 +287,11 @@ void Config::SaveValues() {
283 qt_config->endGroup(); 287 qt_config->endGroup();
284 288
285 qt_config->beginGroup("WebService"); 289 qt_config->beginGroup("WebService");
290 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
286 qt_config->setValue("telemetry_endpoint_url", 291 qt_config->setValue("telemetry_endpoint_url",
287 QString::fromStdString(Settings::values.telemetry_endpoint_url)); 292 QString::fromStdString(Settings::values.telemetry_endpoint_url));
293 qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username));
294 qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token));
288 qt_config->endGroup(); 295 qt_config->endGroup();
289 296
290 qt_config->beginGroup("UI"); 297 qt_config->beginGroup("UI");
@@ -320,6 +327,7 @@ void Config::SaveValues() {
320 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 327 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
321 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); 328 qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
322 qt_config->setValue("firstStart", UISettings::values.first_start); 329 qt_config->setValue("firstStart", UISettings::values.first_start);
330 qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
323 331
324 qt_config->endGroup(); 332 qt_config->endGroup();
325} 333}
diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui
index 85e206e42..6abd1917e 100644
--- a/src/citra_qt/configuration/configure.ui
+++ b/src/citra_qt/configuration/configure.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>441</width> 9 <width>740</width>
10 <height>501</height> 10 <height>500</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -49,6 +49,11 @@
49 <string>Debug</string> 49 <string>Debug</string>
50 </attribute> 50 </attribute>
51 </widget> 51 </widget>
52 <widget class="ConfigureWeb" name="webTab">
53 <attribute name="title">
54 <string>Web</string>
55 </attribute>
56 </widget>
52 </widget> 57 </widget>
53 </item> 58 </item>
54 <item> 59 <item>
@@ -97,6 +102,12 @@
97 <header>configuration/configure_graphics.h</header> 102 <header>configuration/configure_graphics.h</header>
98 <container>1</container> 103 <container>1</container>
99 </customwidget> 104 </customwidget>
105 <customwidget>
106 <class>ConfigureWeb</class>
107 <extends>QWidget</extends>
108 <header>configuration/configure_web.h</header>
109 <container>1</container>
110 </customwidget>
100 </customwidgets> 111 </customwidgets>
101 <resources/> 112 <resources/>
102 <connections> 113 <connections>
diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp
index dfc8c03a7..b87dc0e6c 100644
--- a/src/citra_qt/configuration/configure_dialog.cpp
+++ b/src/citra_qt/configuration/configure_dialog.cpp
@@ -23,5 +23,6 @@ void ConfigureDialog::applyConfiguration() {
23 ui->graphicsTab->applyConfiguration(); 23 ui->graphicsTab->applyConfiguration();
24 ui->audioTab->applyConfiguration(); 24 ui->audioTab->applyConfiguration();
25 ui->debugTab->applyConfiguration(); 25 ui->debugTab->applyConfiguration();
26 ui->webTab->applyConfiguration();
26 Settings::Apply(); 27 Settings::Apply();
27} 28}
diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index 228f2a869..b340149d5 100644
--- a/src/citra_qt/configuration/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -146,17 +146,22 @@
146 <widget class="QComboBox" name="layout_combobox"> 146 <widget class="QComboBox" name="layout_combobox">
147 <item> 147 <item>
148 <property name="text"> 148 <property name="text">
149 <string notr="true">Default</string> 149 <string>Default</string>
150 </property> 150 </property>
151 </item> 151 </item>
152 <item> 152 <item>
153 <property name="text"> 153 <property name="text">
154 <string notr="true">Single Screen</string> 154 <string>Single Screen</string>
155 </property> 155 </property>
156 </item> 156 </item>
157 <item> 157 <item>
158 <property name="text"> 158 <property name="text">
159 <string notr="true">Large Screen</string> 159 <string>Large Screen</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>Side by Side</string>
160 </property> 165 </property>
161 </item> 166 </item>
162 </widget> 167 </widget>
diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp
new file mode 100644
index 000000000..8715fb018
--- /dev/null
+++ b/src/citra_qt/configuration/configure_web.cpp
@@ -0,0 +1,52 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "citra_qt/configuration/configure_web.h"
6#include "core/settings.h"
7#include "core/telemetry_session.h"
8#include "ui_configure_web.h"
9
10ConfigureWeb::ConfigureWeb(QWidget* parent)
11 : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
12 ui->setupUi(this);
13 connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
14 &ConfigureWeb::refreshTelemetryID);
15
16 this->setConfiguration();
17}
18
19ConfigureWeb::~ConfigureWeb() {}
20
21void ConfigureWeb::setConfiguration() {
22 ui->web_credentials_disclaimer->setWordWrap(true);
23 ui->telemetry_learn_more->setOpenExternalLinks(true);
24 ui->telemetry_learn_more->setText("<a "
25 "href='https://citra-emu.org/entry/"
26 "telemetry-and-why-thats-a-good-thing/'>Learn more</a>");
27
28 ui->web_signup_link->setOpenExternalLinks(true);
29 ui->web_signup_link->setText("<a href='https://services.citra-emu.org/'>Sign up</a>");
30 ui->web_token_info_link->setOpenExternalLinks(true);
31 ui->web_token_info_link->setText(
32 "<a href='https://citra-emu.org/wiki/citra-web-service/'>What is my token?</a>");
33
34 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
35 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
36 ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token));
37 ui->label_telemetry_id->setText("Telemetry ID: 0x" +
38 QString::number(Core::GetTelemetryId(), 16).toUpper());
39}
40
41void ConfigureWeb::applyConfiguration() {
42 Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
43 Settings::values.citra_username = ui->edit_username->text().toStdString();
44 Settings::values.citra_token = ui->edit_token->text().toStdString();
45 Settings::Apply();
46}
47
48void ConfigureWeb::refreshTelemetryID() {
49 const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
50 ui->label_telemetry_id->setText("Telemetry ID: 0x" +
51 QString::number(new_telemetry_id, 16).toUpper());
52}
diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h
new file mode 100644
index 000000000..20bc254b9
--- /dev/null
+++ b/src/citra_qt/configuration/configure_web.h
@@ -0,0 +1,30 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureWeb;
12}
13
14class ConfigureWeb : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureWeb(QWidget* parent = nullptr);
19 ~ConfigureWeb();
20
21 void applyConfiguration();
22
23public slots:
24 void refreshTelemetryID();
25
26private:
27 void setConfiguration();
28
29 std::unique_ptr<Ui::ConfigureWeb> ui;
30};
diff --git a/src/citra_qt/configuration/configure_web.ui b/src/citra_qt/configuration/configure_web.ui
new file mode 100644
index 000000000..d8d283fad
--- /dev/null
+++ b/src/citra_qt/configuration/configure_web.ui
@@ -0,0 +1,153 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureWeb</class>
4 <widget class="QWidget" name="ConfigureWeb">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBoxWebConfig">
21 <property name="title">
22 <string>Citra Web Service</string>
23 </property>
24 <layout class="QVBoxLayout" name="verticalLayoutCitraWebService">
25 <item>
26 <widget class="QLabel" name="web_credentials_disclaimer">
27 <property name="text">
28 <string>By providing your username and token, you agree to allow Citra to collect additional usage data, which may include user identifying information.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <layout class="QGridLayout" name="gridLayoutCitraUsername">
34 <item row="0" column="0">
35 <widget class="QLabel" name="label_username">
36 <property name="text">
37 <string>Username: </string>
38 </property>
39 </widget>
40 </item>
41 <item row="0" column="1">
42 <widget class="QLineEdit" name="edit_username">
43 <property name="maxLength">
44 <number>36</number>
45 </property>
46 </widget>
47 </item>
48 <item row="1" column="0">
49 <widget class="QLabel" name="label_token">
50 <property name="text">
51 <string>Token: </string>
52 </property>
53 </widget>
54 </item>
55 <item row="1" column="1">
56 <widget class="QLineEdit" name="edit_token">
57 <property name="maxLength">
58 <number>36</number>
59 </property>
60 <property name="echoMode">
61 <enum>QLineEdit::Password</enum>
62 </property>
63 </widget>
64 </item>
65 <item row="2" column="0">
66 <widget class="QLabel" name="web_signup_link">
67 <property name="text">
68 <string>Sign up</string>
69 </property>
70 </widget>
71 </item>
72 <item row="2" column="1">
73 <widget class="QLabel" name="web_token_info_link">
74 <property name="text">
75 <string>What is my token?</string>
76 </property>
77 </widget>
78 </item>
79 </layout>
80 </item>
81 </layout>
82 </widget>
83 </item>
84 <item>
85 <widget class="QGroupBox" name="groupBox">
86 <property name="title">
87 <string>Telemetry</string>
88 </property>
89 <layout class="QVBoxLayout" name="verticalLayout_2">
90 <item>
91 <widget class="QCheckBox" name="toggle_telemetry">
92 <property name="text">
93 <string>Share anonymous usage data with the Citra team</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QLabel" name="telemetry_learn_more">
99 <property name="text">
100 <string>Learn more</string>
101 </property>
102 </widget>
103 </item>
104 <item>
105 <layout class="QGridLayout" name="gridLayoutTelemetryId">
106 <item row="0" column="0">
107 <widget class="QLabel" name="label_telemetry_id">
108 <property name="text">
109 <string>Telemetry ID:</string>
110 </property>
111 </widget>
112 </item>
113 <item row="0" column="1">
114 <widget class="QPushButton" name="button_regenerate_telemetry_id">
115 <property name="sizePolicy">
116 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
117 <horstretch>0</horstretch>
118 <verstretch>0</verstretch>
119 </sizepolicy>
120 </property>
121 <property name="layoutDirection">
122 <enum>Qt::RightToLeft</enum>
123 </property>
124 <property name="text">
125 <string>Regenerate</string>
126 </property>
127 </widget>
128 </item>
129 </layout>
130 </item>
131 </layout>
132 </widget>
133 </item>
134 </layout>
135 </item>
136 <item>
137 <spacer name="verticalSpacer">
138 <property name="orientation">
139 <enum>Qt::Vertical</enum>
140 </property>
141 <property name="sizeHint" stdset="0">
142 <size>
143 <width>20</width>
144 <height>40</height>
145 </size>
146 </property>
147 </spacer>
148 </item>
149 </layout>
150 </widget>
151 <resources/>
152 <connections/>
153</ui>
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index c1ae0ccc8..8adbcfe86 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -48,6 +48,47 @@
48Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 48Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
49#endif 49#endif
50 50
51/**
52 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
53 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
54 * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones.
55 */
56enum class CalloutFlag : uint32_t {
57 Telemetry = 0x1,
58};
59
60static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
61 if (UISettings::values.callout_flags & static_cast<uint32_t>(flag)) {
62 return;
63 }
64
65 UISettings::values.callout_flags |= static_cast<uint32_t>(flag);
66
67 QMessageBox msg;
68 msg.setText(message);
69 msg.setStandardButtons(QMessageBox::Ok);
70 msg.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
71 msg.setStyleSheet("QLabel{min-width: 900px;}");
72 msg.exec();
73}
74
75void GMainWindow::ShowCallouts() {
76 static const QString telemetry_message =
77 tr("To help improve Citra, the Citra Team collects anonymous usage data. No private or "
78 "personally identifying information is collected. This data helps us to understand how "
79 "people use Citra and prioritize our efforts. Furthermore, it helps us to more easily "
80 "identify emulation bugs and performance issues. This data includes:<ul><li>Information"
81 " about the version of Citra you are using</li><li>Performance data about the games you "
82 "play</li><li>Your configuration settings</li><li>Information about your computer "
83 "hardware</li><li>Emulation errors and crash information</li></ul>By default, this "
84 "feature is enabled. To disable this feature, click 'Emulation' from the menu and then "
85 "select 'Configure...'. Then, on the 'Web' tab, uncheck 'Share anonymous usage data with"
86 " the Citra team'. <br/><br/>By using this software, you agree to the above terms.<br/>"
87 "<br/><a href='https://citra-emu.org/entry/telemetry-and-why-thats-a-good-thing/'>Learn "
88 "more</a>");
89 ShowCalloutMessage(telemetry_message, CalloutFlag::Telemetry);
90}
91
51GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 92GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
52 Pica::g_debug_context = Pica::DebugContext::Construct(); 93 Pica::g_debug_context = Pica::DebugContext::Construct();
53 setAcceptDrops(true); 94 setAcceptDrops(true);
@@ -73,6 +114,9 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
73 114
74 UpdateUITheme(); 115 UpdateUITheme();
75 116
117 // Show one-time "callout" messages to the user
118 ShowCallouts();
119
76 QStringList args = QApplication::arguments(); 120 QStringList args = QApplication::arguments();
77 if (args.length() >= 2) { 121 if (args.length() >= 2) {
78 BootGame(args[1]); 122 BootGame(args[1]);
@@ -320,6 +364,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
320 364
321 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; 365 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
322 366
367 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
368
323 if (result != Core::System::ResultStatus::Success) { 369 if (result != Core::System::ResultStatus::Success) {
324 switch (result) { 370 switch (result) {
325 case Core::System::ResultStatus::ErrorGetLoader: 371 case Core::System::ResultStatus::ErrorGetLoader:
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 360de2ced..d59a6d67d 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -80,6 +80,8 @@ private:
80 void BootGame(const QString& filename); 80 void BootGame(const QString& filename);
81 void ShutdownGame(); 81 void ShutdownGame();
82 82
83 void ShowCallouts();
84
83 /** 85 /**
84 * Stores the filename in the recently loaded files list. 86 * Stores the filename in the recently loaded files list.
85 * The new filename is stored at the beginning of the recently loaded files list. 87 * The new filename is stored at the beginning of the recently loaded files list.
diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h
index 025c73f84..d85c92765 100644
--- a/src/citra_qt/ui_settings.h
+++ b/src/citra_qt/ui_settings.h
@@ -48,6 +48,8 @@ struct Values {
48 48
49 // Shortcut name <Shortcut, context> 49 // Shortcut name <Shortcut, context>
50 std::vector<Shortcut> shortcuts; 50 std::vector<Shortcut> shortcuts;
51
52 uint32_t callout_flags;
51}; 53};
52 54
53extern Values values; 55extern Values values;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 53bd50eb2..662030782 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -59,6 +59,7 @@ set(SRCS
59 hle/kernel/timer.cpp 59 hle/kernel/timer.cpp
60 hle/kernel/vm_manager.cpp 60 hle/kernel/vm_manager.cpp
61 hle/kernel/wait_object.cpp 61 hle/kernel/wait_object.cpp
62 hle/lock.cpp
62 hle/romfs.cpp 63 hle/romfs.cpp
63 hle/service/ac/ac.cpp 64 hle/service/ac/ac.cpp
64 hle/service/ac/ac_i.cpp 65 hle/service/ac/ac_i.cpp
@@ -256,6 +257,7 @@ set(HEADERS
256 hle/kernel/timer.h 257 hle/kernel/timer.h
257 hle/kernel/vm_manager.h 258 hle/kernel/vm_manager.h
258 hle/kernel/wait_object.h 259 hle/kernel/wait_object.h
260 hle/lock.h
259 hle/result.h 261 hle/result.h
260 hle/romfs.h 262 hle/romfs.h
261 hle/service/ac/ac.h 263 hle/service/ac/ac.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 7d2790b08..0a0b91590 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -136,7 +136,7 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
136void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { 136void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
137 MICROPROFILE_SCOPE(ARM_Jit); 137 MICROPROFILE_SCOPE(ARM_Jit);
138 138
139 unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); 139 std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
140 140
141 AddTicks(ticks_executed); 141 AddTicks(ticks_executed);
142} 142}
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index f4fbb8d04..3522d1e82 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -759,7 +759,7 @@ static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_ins
759 ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size); 759 ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size);
760 if (ret == ThumbDecodeStatus::BRANCH) { 760 if (ret == ThumbDecodeStatus::BRANCH) {
761 int inst_index; 761 int inst_index;
762 int table_length = arm_instruction_trans_len; 762 int table_length = static_cast<int>(arm_instruction_trans_len);
763 u32 tinstr = GetThumbInstruction(inst, addr); 763 u32 tinstr = GetThumbInstruction(inst, addr);
764 764
765 switch ((tinstr & 0xF800) >> 11) { 765 switch ((tinstr & 0xF800) >> 11) {
@@ -838,7 +838,7 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons
838 return inst_size; 838 return inst_size;
839} 839}
840 840
841static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { 841static int InterpreterTranslateBlock(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
842 MICROPROFILE_SCOPE(DynCom_Decode); 842 MICROPROFILE_SCOPE(DynCom_Decode);
843 843
844 // Decode instruction, get index 844 // Decode instruction, get index
@@ -871,7 +871,7 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr)
871 return KEEP_GOING; 871 return KEEP_GOING;
872} 872}
873 873
874static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { 874static int InterpreterTranslateSingle(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
875 MICROPROFILE_SCOPE(DynCom_Decode); 875 MICROPROFILE_SCOPE(DynCom_Decode);
876 876
877 ARM_INST_PTR inst_base = nullptr; 877 ARM_INST_PTR inst_base = nullptr;
@@ -1620,7 +1620,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
1620 unsigned int addr; 1620 unsigned int addr;
1621 unsigned int num_instrs = 0; 1621 unsigned int num_instrs = 0;
1622 1622
1623 int ptr; 1623 std::size_t ptr;
1624 1624
1625 LOAD_NZCVT; 1625 LOAD_NZCVT;
1626DISPATCH : { 1626DISPATCH : {
diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h
index 1a707ff7e..893877797 100644
--- a/src/core/arm/skyeye_common/armstate.h
+++ b/src/core/arm/skyeye_common/armstate.h
@@ -230,7 +230,7 @@ public:
230 230
231 // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per 231 // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per
232 // process for our purposes), not per ARMul_State (which tracks CPU core state). 232 // process for our purposes), not per ARMul_State (which tracks CPU core state).
233 std::unordered_map<u32, int> instruction_cache; 233 std::unordered_map<u32, std::size_t> instruction_cache;
234 234
235private: 235private:
236 void ResetMPCoreCP15Registers(); 236 void ResetMPCoreCP15Registers();
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 1fae0ede0..87a240d7a 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -90,6 +90,8 @@ std::u16string Path::AsU16Str() const {
90 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); 90 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
91 return {}; 91 return {};
92 } 92 }
93
94 UNREACHABLE();
93} 95}
94 96
95std::vector<u8> Path::AsBinary() const { 97std::vector<u8> Path::AsBinary() const {
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 60b20d4e2..54fa5c7fa 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -74,6 +74,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
74 case Settings::LayoutOption::LargeScreen: 74 case Settings::LayoutOption::LargeScreen:
75 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); 75 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
76 break; 76 break;
77 case Settings::LayoutOption::SideScreen:
78 layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen);
79 break;
77 case Settings::LayoutOption::Default: 80 case Settings::LayoutOption::Default:
78 default: 81 default:
79 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); 82 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d2d02f9ff..e9f778fcb 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
141 return res; 141 return res;
142} 142}
143 143
144FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) {
145 ASSERT(width > 0);
146 ASSERT(height > 0);
147
148 FramebufferLayout res{width, height, true, true, {}, {}};
149 // Aspect ratio of both screens side by side
150 const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
151 (Core::kScreenTopWidth + Core::kScreenBottomWidth);
152 float window_aspect_ratio = static_cast<float>(height) / width;
153 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
154 // Find largest Rectangle that can fit in the window size with the given aspect ratio
155 MathUtil::Rectangle<unsigned> screen_rect =
156 maxRectangle(screen_window_area, emulation_aspect_ratio);
157 // Find sizes of top and bottom screen
158 MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
159 MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
160
161 if (window_aspect_ratio < emulation_aspect_ratio) {
162 // Apply borders to the left and right sides of the window.
163 u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
164 top_screen = top_screen.TranslateX(shift_horizontal);
165 bot_screen = bot_screen.TranslateX(shift_horizontal);
166 } else {
167 // Window is narrower than the emulation content => apply borders to the top and bottom
168 u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
169 top_screen = top_screen.TranslateY(shift_vertical);
170 bot_screen = bot_screen.TranslateY(shift_vertical);
171 }
172 // Move the top screen to the right if we are swapped.
173 res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
174 res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
175 return res;
176}
177
144FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { 178FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
145 ASSERT(width > 0); 179 ASSERT(width > 0);
146 ASSERT(height > 0); 180 ASSERT(height > 0);
@@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
158 res.bottom_screen = bot_screen; 192 res.bottom_screen = bot_screen;
159 return res; 193 return res;
160} 194}
161} 195} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 9a7738969..4983cf103 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -54,6 +54,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa
54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); 54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
55 55
56/** 56/**
57* Factory method for constructing a Frame with the Top screen and bottom
58* screen side by side
59* This is useful for devices with small screens, like the GPDWin
60* @param width Window framebuffer width in pixels
61* @param height Window framebuffer height in pixels
62* @param is_swapped if true, the bottom screen will be the left display
63* @return Newly created FramebufferLayout object with default screen regions initialized
64*/
65FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped);
66
67/**
57 * Factory method for constructing a custom FramebufferLayout 68 * Factory method for constructing a custom FramebufferLayout
58 * @param width Window framebuffer width in pixels 69 * @param width Window framebuffer width in pixels
59 * @param height Window framebuffer height in pixels 70 * @param height Window framebuffer height in pixels
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 75d7fd9fc..518f371f5 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -31,8 +31,8 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "ErrEula Memory"); 35 "ErrEula Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 89f08daa2..705859f1e 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -38,8 +38,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
39 // Create a SharedMemory that directly points to this heap block. 39 // Create a SharedMemory that directly points to this heap block.
40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
41 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 41 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
42 MemoryPermission::ReadWrite, "MiiSelector Memory"); 42 "MiiSelector Memory");
43 43
44 // Send the response message with the newly created SharedMemory 44 // Send the response message with the newly created SharedMemory
45 Service::APT::MessageParameter result; 45 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp
index 31a79ea17..50d79190b 100644
--- a/src/core/hle/applets/mint.cpp
+++ b/src/core/hle/applets/mint.cpp
@@ -31,8 +31,8 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "Mint Memory"); 35 "Mint Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index fdf8807b0..0bc471a3a 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -41,8 +41,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
42 // Create a SharedMemory that directly points to this heap block. 42 // Create a SharedMemory that directly points to this heap block.
43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
44 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 44 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
45 MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); 45 "SoftwareKeyboard Memory");
46 46
47 // Send the response message with the newly created SharedMemory 47 // Send the response message with the newly created SharedMemory
48 Service::APT::MessageParameter result; 48 Service::APT::MessageParameter result;
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 9cf288b08..73fab3981 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <utility> 9#include <utility>
10#include <boost/smart_ptr/intrusive_ptr.hpp> 10#include <boost/smart_ptr/intrusive_ptr.hpp>
11#include "common/assert.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
13namespace Kernel { 14namespace Kernel {
@@ -84,6 +85,8 @@ public:
84 case HandleType::ClientSession: 85 case HandleType::ClientSession:
85 return false; 86 return false;
86 } 87 }
88
89 UNREACHABLE();
87 } 90 }
88 91
89public: 92public:
@@ -129,4 +132,4 @@ void Init(u32 system_mode);
129/// Shutdown the kernel 132/// Shutdown the kernel
130void Shutdown(); 133void Shutdown();
131 134
132} // namespace 135} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f5f2eb2f7..b957c45dd 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -478,8 +478,6 @@ void Thread::BoostPriority(s32 priority) {
478} 478}
479 479
480SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { 480SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
481 DEBUG_ASSERT(!GetCurrentThread());
482
483 // Initialize new "main" thread 481 // Initialize new "main" thread
484 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 482 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
485 Memory::HEAP_VADDR_END); 483 Memory::HEAP_VADDR_END);
@@ -489,9 +487,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
489 thread->context.fpscr = 487 thread->context.fpscr =
490 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 488 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
491 489
492 // Run new "main" thread 490 // Note: The newly created thread will be run when the scheduler fires.
493 SwitchContext(thread.get());
494
495 return thread; 491 return thread;
496} 492}
497 493
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
new file mode 100644
index 000000000..082f689c8
--- /dev/null
+++ b/src/core/hle/lock.cpp
@@ -0,0 +1,11 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <core/hle/lock.h>
8
9namespace HLE {
10std::mutex g_hle_lock;
11}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
new file mode 100644
index 000000000..8265621e1
--- /dev/null
+++ b/src/core/hle/lock.h
@@ -0,0 +1,18 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8
9namespace HLE {
10/*
11 * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
12 * application thread performs a syscall. It should be acquired by any host threads that read or
13 * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
15 * than the CPU thread.
16 */
17extern std::mutex g_hle_lock;
18} // namespace HLE
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index e4b803046..b98938cb4 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -31,6 +31,7 @@
31#include "core/hle/kernel/timer.h" 31#include "core/hle/kernel/timer.h"
32#include "core/hle/kernel/vm_manager.h" 32#include "core/hle/kernel/vm_manager.h"
33#include "core/hle/kernel/wait_object.h" 33#include "core/hle/kernel/wait_object.h"
34#include "core/hle/lock.h"
34#include "core/hle/result.h" 35#include "core/hle/result.h"
35#include "core/hle/service/service.h" 36#include "core/hle/service/service.h"
36 37
@@ -1188,7 +1189,7 @@ struct FunctionDef {
1188 Func* func; 1189 Func* func;
1189 const char* name; 1190 const char* name;
1190}; 1191};
1191} 1192} // namespace
1192 1193
1193static const FunctionDef SVC_Table[] = { 1194static const FunctionDef SVC_Table[] = {
1194 {0x00, nullptr, "Unknown"}, 1195 {0x00, nullptr, "Unknown"},
@@ -1332,6 +1333,9 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
1332void CallSVC(u32 immediate) { 1333void CallSVC(u32 immediate) {
1333 MICROPROFILE_SCOPE(Kernel_SVC); 1334 MICROPROFILE_SCOPE(Kernel_SVC);
1334 1335
1336 // Lock the global kernel mutex when we enter the kernel HLE.
1337 std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
1338
1335 const FunctionDef* info = GetSVCInfo(immediate); 1339 const FunctionDef* info = GetSVCInfo(immediate);
1336 if (info) { 1340 if (info) {
1337 if (info->func) { 1341 if (info->func) {
@@ -1342,4 +1346,4 @@ void CallSVC(u32 immediate) {
1342 } 1346 }
1343} 1347}
1344 1348
1345} // namespace 1349} // namespace SVC
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 6838e449c..83ad9d898 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,7 +29,7 @@ namespace GPU {
29Regs g_regs; 29Regs g_regs;
30 30
31/// 268MHz CPU clocks / 60Hz frames per second 31/// 268MHz CPU clocks / 60Hz frames per second
32const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; 32const u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE);
33/// Event id for CoreTiming 33/// Event id for CoreTiming
34static int vblank_event; 34static int vblank_event;
35 35
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 21b127fee..e3d0a0e08 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -74,9 +74,9 @@ struct Regs {
74 case PixelFormat::RGB5A1: 74 case PixelFormat::RGB5A1:
75 case PixelFormat::RGBA4: 75 case PixelFormat::RGBA4:
76 return 2; 76 return 2;
77 default:
78 UNIMPLEMENTED();
79 } 77 }
78
79 UNREACHABLE();
80 } 80 }
81 81
82 INSERT_PADDING_WORDS(0x4); 82 INSERT_PADDING_WORDS(0x4);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 65649d9d7..a3c5f4a9d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/hle/lock.h"
12#include "core/memory.h" 13#include "core/memory.h"
13#include "core/memory_setup.h" 14#include "core/memory_setup.h"
14#include "core/mmio.h" 15#include "core/mmio.h"
@@ -181,6 +182,9 @@ T Read(const VAddr vaddr) {
181 return value; 182 return value;
182 } 183 }
183 184
185 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
186 std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
187
184 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 188 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
185 switch (type) { 189 switch (type) {
186 case PageType::Unmapped: 190 case PageType::Unmapped:
@@ -219,6 +223,9 @@ void Write(const VAddr vaddr, const T data) {
219 return; 223 return;
220 } 224 }
221 225
226 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
227 std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
228
222 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 229 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
223 switch (type) { 230 switch (type) {
224 case PageType::Unmapped: 231 case PageType::Unmapped:
@@ -746,4 +753,4 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
746 return boost::none; 753 return boost::none;
747} 754}
748 755
749} // namespace 756} // namespace Memory
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d4f0429d1..efcf1267d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -36,4 +36,4 @@ void Apply() {
36 Service::IR::ReloadInputDevices(); 36 Service::IR::ReloadInputDevices();
37} 37}
38 38
39} // namespace 39} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 7e15b119b..bf8014c5a 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -15,6 +15,7 @@ enum class LayoutOption {
15 Default, 15 Default,
16 SingleScreen, 16 SingleScreen,
17 LargeScreen, 17 LargeScreen,
18 SideScreen,
18}; 19};
19 20
20namespace NativeButton { 21namespace NativeButton {
@@ -70,7 +71,7 @@ enum Values {
70static const std::array<const char*, NumAnalogs> mapping = {{ 71static const std::array<const char*, NumAnalogs> mapping = {{
71 "circle_pad", "c_stick", 72 "circle_pad", "c_stick",
72}}; 73}};
73} // namespace NumAnalog 74} // namespace NativeAnalog
74 75
75struct Values { 76struct Values {
76 // CheckNew3DS 77 // CheckNew3DS
@@ -129,7 +130,10 @@ struct Values {
129 u16 gdbstub_port; 130 u16 gdbstub_port;
130 131
131 // WebService 132 // WebService
133 bool enable_telemetry;
132 std::string telemetry_endpoint_url; 134 std::string telemetry_endpoint_url;
135 std::string citra_username;
136 std::string citra_token;
133} extern values; 137} extern values;
134 138
135// a special value for Values::region_value indicating that citra will automatically select a region 139// a special value for Values::region_value indicating that citra will automatically select a region
@@ -137,4 +141,4 @@ struct Values {
137static constexpr int REGION_VALUE_AUTO_SELECT = -1; 141static constexpr int REGION_VALUE_AUTO_SELECT = -1;
138 142
139void Apply(); 143void Apply();
140} 144} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 94483f385..104a16cc9 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -3,8 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <cryptopp/osrng.h>
6 7
7#include "common/assert.h" 8#include "common/assert.h"
9#include "common/file_util.h"
8#include "common/scm_rev.h" 10#include "common/scm_rev.h"
9#include "common/x64/cpu_detect.h" 11#include "common/x64/cpu_detect.h"
10#include "core/core.h" 12#include "core/core.h"
@@ -29,12 +31,65 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) {
29 UNREACHABLE(); 31 UNREACHABLE();
30} 32}
31 33
34static u64 GenerateTelemetryId() {
35 u64 telemetry_id{};
36 CryptoPP::AutoSeededRandomPool rng;
37 rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&telemetry_id), sizeof(u64));
38 return telemetry_id;
39}
40
41u64 GetTelemetryId() {
42 u64 telemetry_id{};
43 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
44
45 if (FileUtil::Exists(filename)) {
46 FileUtil::IOFile file(filename, "rb");
47 if (!file.IsOpen()) {
48 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
49 return {};
50 }
51 file.ReadBytes(&telemetry_id, sizeof(u64));
52 } else {
53 FileUtil::IOFile file(filename, "wb");
54 if (!file.IsOpen()) {
55 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
56 return {};
57 }
58 telemetry_id = GenerateTelemetryId();
59 file.WriteBytes(&telemetry_id, sizeof(u64));
60 }
61
62 return telemetry_id;
63}
64
65u64 RegenerateTelemetryId() {
66 const u64 new_telemetry_id{GenerateTelemetryId()};
67 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
68
69 FileUtil::IOFile file(filename, "wb");
70 if (!file.IsOpen()) {
71 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
72 return {};
73 }
74 file.WriteBytes(&new_telemetry_id, sizeof(u64));
75 return new_telemetry_id;
76}
77
32TelemetrySession::TelemetrySession() { 78TelemetrySession::TelemetrySession() {
33#ifdef ENABLE_WEB_SERVICE 79#ifdef ENABLE_WEB_SERVICE
34 backend = std::make_unique<WebService::TelemetryJson>(); 80 if (Settings::values.enable_telemetry) {
81 backend = std::make_unique<WebService::TelemetryJson>(
82 Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
83 Settings::values.citra_token);
84 } else {
85 backend = std::make_unique<Telemetry::NullVisitor>();
86 }
35#else 87#else
36 backend = std::make_unique<Telemetry::NullVisitor>(); 88 backend = std::make_unique<Telemetry::NullVisitor>();
37#endif 89#endif
90 // Log one-time top-level information
91 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
92
38 // Log one-time session start information 93 // Log one-time session start information
39 const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>( 94 const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
40 std::chrono::system_clock::now().time_since_epoch()) 95 std::chrono::system_clock::now().time_since_epoch())
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cf53835c3..65613daae 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -35,4 +35,16 @@ private:
35 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields 35 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
36}; 36};
37 37
38/**
39 * Gets TelemetryId, a unique identifier used for the user's telemetry sessions.
40 * @returns The current TelemetryId for the session.
41 */
42u64 GetTelemetryId();
43
44/**
45 * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions.
46 * @returns The new TelemetryId that was generated.
47 */
48u64 RegenerateTelemetryId();
49
38} // namespace Core 50} // namespace Core
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index a1761f184..59a035e70 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -74,11 +74,14 @@ private:
74 bool is_tilting = false; 74 bool is_tilting = false;
75 75
76 Common::Event shutdown_event; 76 Common::Event shutdown_event;
77 std::thread motion_emu_thread;
78 77
79 std::tuple<Math::Vec3<float>, Math::Vec3<float>> status; 78 std::tuple<Math::Vec3<float>, Math::Vec3<float>> status;
80 std::mutex status_mutex; 79 std::mutex status_mutex;
81 80
81 // Note: always keep the thread declaration at the end so that other objects are initialized
82 // before this!
83 std::thread motion_emu_thread;
84
82 void MotionEmuThread() { 85 void MotionEmuThread() {
83 auto update_time = std::chrono::steady_clock::now(); 86 auto update_time = std::chrono::steady_clock::now();
84 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0); 87 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1c6c15a58..aa95ef21d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -28,6 +28,9 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
28MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 28MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
29 29
30RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { 30RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
31 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
32 state.clip_distance[0] = true;
33
31 // Create sampler objects 34 // Create sampler objects
32 for (size_t i = 0; i < texture_samplers.size(); ++i) { 35 for (size_t i = 0; i < texture_samplers.size(); ++i) {
33 texture_samplers[i].Create(); 36 texture_samplers[i].Create();
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index ae67aab05..015e69da9 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -1112,7 +1112,10 @@ vec4 secondary_fragment_color = vec4(0.0);
1112 "gl_FragCoord.y < scissor_y2)) discard;\n"; 1112 "gl_FragCoord.y < scissor_y2)) discard;\n";
1113 } 1113 }
1114 1114
1115 out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; 1115 // After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use
1116 // default near = 0 and far = 1, and undo the transformation to get the original z_over_w, then
1117 // do our own transformation according to PICA specification.
1118 out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n";
1116 out += "float depth = z_over_w * depth_scale + depth_offset;\n"; 1119 out += "float depth = z_over_w * depth_scale + depth_offset;\n";
1117 if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) { 1120 if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
1118 out += "depth /= gl_FragCoord.w;\n"; 1121 out += "depth /= gl_FragCoord.w;\n";
@@ -1195,7 +1198,9 @@ void main() {
1195 texcoord0_w = vert_texcoord0_w; 1198 texcoord0_w = vert_texcoord0_w;
1196 normquat = vert_normquat; 1199 normquat = vert_normquat;
1197 view = vert_view; 1200 view = vert_view;
1198 gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w); 1201 gl_Position = vert_position;
1202 gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
1203 // TODO (wwylele): calculate gl_ClipDistance[1] from user-defined clipping plane
1199} 1204}
1200)"; 1205)";
1201 1206
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index bc9d34b84..06a905766 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -68,6 +68,8 @@ OpenGLState::OpenGLState() {
68 draw.vertex_buffer = 0; 68 draw.vertex_buffer = 0;
69 draw.uniform_buffer = 0; 69 draw.uniform_buffer = 0;
70 draw.shader_program = 0; 70 draw.shader_program = 0;
71
72 clip_distance = {};
71} 73}
72 74
73void OpenGLState::Apply() const { 75void OpenGLState::Apply() const {
@@ -261,6 +263,17 @@ void OpenGLState::Apply() const {
261 glUseProgram(draw.shader_program); 263 glUseProgram(draw.shader_program);
262 } 264 }
263 265
266 // Clip distance
267 for (size_t i = 0; i < clip_distance.size(); ++i) {
268 if (clip_distance[i] != cur_state.clip_distance[i]) {
269 if (clip_distance[i]) {
270 glEnable(GL_CLIP_DISTANCE0 + i);
271 } else {
272 glDisable(GL_CLIP_DISTANCE0 + i);
273 }
274 }
275 }
276
264 cur_state = *this; 277 cur_state = *this;
265} 278}
266 279
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 745a74479..437fe34c4 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <glad/glad.h> 8#include <glad/glad.h>
8 9
9namespace TextureUnits { 10namespace TextureUnits {
@@ -123,6 +124,8 @@ public:
123 GLuint shader_program; // GL_CURRENT_PROGRAM 124 GLuint shader_program; // GL_CURRENT_PROGRAM
124 } draw; 125 } draw;
125 126
127 std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
128
126 OpenGLState(); 129 OpenGLState();
127 130
128 /// Get the currently active OpenGL state 131 /// Get the currently active OpenGL state
diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp
index 7537689b7..cdbc71502 100644
--- a/src/video_core/swrasterizer/clipper.cpp
+++ b/src/video_core/swrasterizer/clipper.cpp
@@ -125,10 +125,6 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
125 {Math::MakeVec(f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON 125 {Math::MakeVec(f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON
126 }}; 126 }};
127 127
128 // TODO: If one vertex lies outside one of the depth clipping planes, some platforms (e.g. Wii)
129 // drop the whole primitive instead of clipping the primitive properly. We should test if
130 // this happens on the 3DS, too.
131
132 // Simple implementation of the Sutherland-Hodgman clipping algorithm. 128 // Simple implementation of the Sutherland-Hodgman clipping algorithm.
133 // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here) 129 // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
134 for (auto edge : clipping_edges) { 130 for (auto edge : clipping_edges) {
diff --git a/src/video_core/swrasterizer/rasterizer.h b/src/video_core/swrasterizer/rasterizer.h
index 2f0877581..66cd6cfd4 100644
--- a/src/video_core/swrasterizer/rasterizer.h
+++ b/src/video_core/swrasterizer/rasterizer.h
@@ -19,10 +19,9 @@ struct Vertex : Shader::OutputVertex {
19 19
20 // Linear interpolation 20 // Linear interpolation
21 // factor: 0=this, 1=vtx 21 // factor: 0=this, 1=vtx
22 // Note: This function cannot be called after perspective divide
22 void Lerp(float24 factor, const Vertex& vtx) { 23 void Lerp(float24 factor, const Vertex& vtx) {
23 pos = pos * factor + vtx.pos * (float24::FromFloat32(1) - factor); 24 pos = pos * factor + vtx.pos * (float24::FromFloat32(1) - factor);
24
25 // TODO: Should perform perspective correct interpolation here...
26 quat = quat * factor + vtx.quat * (float24::FromFloat32(1) - factor); 25 quat = quat * factor + vtx.quat * (float24::FromFloat32(1) - factor);
27 color = color * factor + vtx.color * (float24::FromFloat32(1) - factor); 26 color = color * factor + vtx.color * (float24::FromFloat32(1) - factor);
28 tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); 27 tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor);
@@ -30,12 +29,11 @@ struct Vertex : Shader::OutputVertex {
30 tc0_w = tc0_w * factor + vtx.tc0_w * (float24::FromFloat32(1) - factor); 29 tc0_w = tc0_w * factor + vtx.tc0_w * (float24::FromFloat32(1) - factor);
31 view = view * factor + vtx.view * (float24::FromFloat32(1) - factor); 30 view = view * factor + vtx.view * (float24::FromFloat32(1) - factor);
32 tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor); 31 tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor);
33
34 screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor);
35 } 32 }
36 33
37 // Linear interpolation 34 // Linear interpolation
38 // factor: 0=v0, 1=v1 35 // factor: 0=v0, 1=v1
36 // Note: This function cannot be called after perspective divide
39 static Vertex Lerp(float24 factor, const Vertex& v0, const Vertex& v1) { 37 static Vertex Lerp(float24 factor, const Vertex& v0, const Vertex& v1) {
40 Vertex ret = v0; 38 Vertex ret = v0;
41 ret.Lerp(factor, v1); 39 ret.Lerp(factor, v1);
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index a2d007e77..6ad2ffcd4 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/settings.h"
7#include "web_service/telemetry_json.h" 6#include "web_service/telemetry_json.h"
8#include "web_service/web_backend.h" 7#include "web_service/web_backend.h"
9 8
@@ -81,7 +80,7 @@ void TelemetryJson::Complete() {
81 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 80 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
82 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 81 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
83 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 82 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
84 PostJson(Settings::values.telemetry_endpoint_url, TopSection().dump()); 83 PostJson(endpoint_url, TopSection().dump(), true, username, token);
85} 84}
86 85
87} // namespace WebService 86} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 39038b4f9..9e78c6803 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -17,7 +17,9 @@ namespace WebService {
17 */ 17 */
18class TelemetryJson : public Telemetry::VisitorInterface { 18class TelemetryJson : public Telemetry::VisitorInterface {
19public: 19public:
20 TelemetryJson() = default; 20 TelemetryJson(const std::string& endpoint_url, const std::string& username,
21 const std::string& token)
22 : endpoint_url(endpoint_url), username(username), token(token) {}
21 ~TelemetryJson() = default; 23 ~TelemetryJson() = default;
22 24
23 void Visit(const Telemetry::Field<bool>& field) override; 25 void Visit(const Telemetry::Field<bool>& field) override;
@@ -49,6 +51,9 @@ private:
49 51
50 nlohmann::json output; 52 nlohmann::json output;
51 std::array<nlohmann::json, 7> sections; 53 std::array<nlohmann::json, 7> sections;
54 std::string endpoint_url;
55 std::string username;
56 std::string token;
52}; 57};
53 58
54} // namespace WebService 59} // namespace WebService
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 13e4555ac..d28a3f757 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -2,51 +2,62 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#ifdef _WIN32
6#include <winsock.h>
7#endif
8
9#include <cstdlib>
10#include <thread>
5#include <cpr/cpr.h> 11#include <cpr/cpr.h>
6#include <stdlib.h>
7#include "common/logging/log.h" 12#include "common/logging/log.h"
8#include "web_service/web_backend.h" 13#include "web_service/web_backend.h"
9 14
10namespace WebService { 15namespace WebService {
11 16
12static constexpr char API_VERSION[]{"1"}; 17static constexpr char API_VERSION[]{"1"};
13static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"};
14static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"};
15
16static std::string GetEnvironmentVariable(const char* name) {
17 const char* value{getenv(name)};
18 if (value) {
19 return value;
20 }
21 return {};
22}
23
24const std::string& GetUsername() {
25 static const std::string username{GetEnvironmentVariable(ENV_VAR_USERNAME)};
26 return username;
27}
28 18
29const std::string& GetToken() { 19static std::unique_ptr<cpr::Session> g_session;
30 static const std::string token{GetEnvironmentVariable(ENV_VAR_TOKEN)};
31 return token;
32}
33 20
34void PostJson(const std::string& url, const std::string& data) { 21void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
22 const std::string& username, const std::string& token) {
35 if (url.empty()) { 23 if (url.empty()) {
36 LOG_ERROR(WebService, "URL is invalid"); 24 LOG_ERROR(WebService, "URL is invalid");
37 return; 25 return;
38 } 26 }
39 27
40 if (GetUsername().empty() || GetToken().empty()) { 28 const bool are_credentials_provided{!token.empty() && !username.empty()};
41 LOG_ERROR(WebService, "Environment variables %s and %s must be set to POST JSON", 29 if (!allow_anonymous && !are_credentials_provided) {
42 ENV_VAR_USERNAME, ENV_VAR_TOKEN); 30 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return; 31 return;
44 } 32 }
45 33
46 cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"}, 34#ifdef _WIN32
47 {"x-username", GetUsername()}, 35 // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
48 {"x-token", GetToken()}, 36 // initialize Winsock globally, which fixes this problem. Without this, only the first CPR
49 {"api-version", API_VERSION}}); 37 // session will properly be created, and subsequent ones will fail.
38 WSADATA wsa_data;
39 const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
40 if (wsa_result) {
41 LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
42 }
43#endif
44
45 // Built request header
46 cpr::Header header;
47 if (are_credentials_provided) {
48 // Authenticated request if credentials are provided
49 header = {{"Content-Type", "application/json"},
50 {"x-username", username.c_str()},
51 {"x-token", token.c_str()},
52 {"api-version", API_VERSION}};
53 } else {
54 // Otherwise, anonymous request
55 header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}};
56 }
57
58 // Post JSON asynchronously
59 static cpr::AsyncResponse future;
60 future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header);
50} 61}
51 62
52} // namespace WebService 63} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index 2753d3b68..d17100398 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -10,22 +10,14 @@
10namespace WebService { 10namespace WebService {
11 11
12/** 12/**
13 * Gets the current username for accessing services.citra-emu.org.
14 * @returns Username as a string, empty if not set.
15 */
16const std::string& GetUsername();
17
18/**
19 * Gets the current token for accessing services.citra-emu.org.
20 * @returns Token as a string, empty if not set.
21 */
22const std::string& GetToken();
23
24/**
25 * Posts JSON to services.citra-emu.org. 13 * Posts JSON to services.citra-emu.org.
26 * @param url URL of the services.citra-emu.org endpoint to post data to. 14 * @param url URL of the services.citra-emu.org endpoint to post data to.
27 * @param data String of JSON data to use for the body of the POST request. 15 * @param data String of JSON data to use for the body of the POST request.
16 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
17 * @param username Citra username to use for authentication.
18 * @param token Citra token to use for authentication.
28 */ 19 */
29void PostJson(const std::string& url, const std::string& data); 20void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
21 const std::string& username = {}, const std::string& token = {});
30 22
31} // namespace WebService 23} // namespace WebService