mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-04 15:50:42 -07:00
5cabd026ae
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change - Replace unnecessary precision timers with standard sleeps to reduce wakeups and background CPU usage. - Reuse buffers in raw input, touchscreen, and HID output paths to eliminate steady-state allocations. - Pre-index HID button groups and correctly process batched HID reports. No functional changes. ## Testing
724 lines
26 KiB
C++
724 lines
26 KiB
C++
#include "touch.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <windows.h>
|
|
#include <versionhelpers.h>
|
|
|
|
#include "util/logging.h"
|
|
#include "util/time.h"
|
|
#include "touch/touch.h"
|
|
|
|
// std::min
|
|
#ifdef min
|
|
#undef min
|
|
#endif
|
|
|
|
// std::max
|
|
#ifdef max
|
|
#undef max
|
|
#endif
|
|
|
|
namespace rawinput::touch {
|
|
|
|
// settings
|
|
bool DISABLED = false;
|
|
bool INVERTED = false;
|
|
AspectMode ASPECT_COMPENSATION_MODE = AspectMode::Auto;
|
|
bool ASPECT_COMPENSATION_GAME = false;
|
|
|
|
// state
|
|
DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
|
long DISPLAY_SIZE_X = 1920L;
|
|
long DISPLAY_SIZE_Y = 1080L;
|
|
long DISPLAY_NATIVE_X = 0L;
|
|
long DISPLAY_NATIVE_Y = 0L;
|
|
bool DISPLAY_INITIALIZED = false;
|
|
|
|
static void update_current_display_mode();
|
|
|
|
bool aspect_compensation_enabled() {
|
|
switch (ASPECT_COMPENSATION_MODE) {
|
|
case AspectMode::On:
|
|
return true;
|
|
case AspectMode::Off:
|
|
return false;
|
|
default:
|
|
return ASPECT_COMPENSATION_GAME;
|
|
}
|
|
}
|
|
|
|
// compute the centered letterbox inset (fraction cropped from each side) that results
|
|
// from showing the current display mode on a panel with a different native aspect
|
|
// ratio. only one of inset_x / inset_y is ever non-zero.
|
|
static void compute_letterbox_inset(double &inset_x, double &inset_y) {
|
|
inset_x = 0.0;
|
|
inset_y = 0.0;
|
|
|
|
// native resolution in the current display orientation
|
|
long native_x = DISPLAY_NATIVE_X;
|
|
long native_y = DISPLAY_NATIVE_Y;
|
|
if ((DISPLAY_SIZE_Y >= DISPLAY_SIZE_X) != (native_y >= native_x)) {
|
|
long tmp = native_x;
|
|
native_x = native_y;
|
|
native_y = tmp;
|
|
}
|
|
|
|
// compare the displayed aspect ratio against the panel's native one; ignore
|
|
// tiny mismatches (e.g. 1366x768 vs true 16:9) that would only add a sub-pixel
|
|
// inset, so near-matched panels stay an exact no-op
|
|
double current_aspect = (double) DISPLAY_SIZE_X / (double) DISPLAY_SIZE_Y;
|
|
double native_aspect = (double) native_x / (double) native_y;
|
|
constexpr double aspect_epsilon = 0.01;
|
|
if (current_aspect < native_aspect - aspect_epsilon) {
|
|
|
|
// image is narrower than the panel -> bars on left/right
|
|
inset_x = (1.0 - current_aspect / native_aspect) / 2.0;
|
|
} else if (current_aspect > native_aspect + aspect_epsilon) {
|
|
|
|
// image is wider than the panel -> bars on top/bottom
|
|
inset_y = (1.0 - native_aspect / current_aspect) / 2.0;
|
|
}
|
|
}
|
|
|
|
// undo letterboxing: remap the normalized (0.0 - 1.0) digitizer coordinates, which span
|
|
// the whole physical panel, into the displayed image region. returns false when the
|
|
// touch lands in the black bars (outside the image) and should be ignored.
|
|
static bool remap_aspect_compensation(double &norm_x, double &norm_y) {
|
|
if (DISPLAY_NATIVE_X <= 0 || DISPLAY_NATIVE_Y <= 0) {
|
|
return true;
|
|
}
|
|
|
|
double inset_x, inset_y;
|
|
compute_letterbox_inset(inset_x, inset_y);
|
|
|
|
// log once the first time the compensation actually engages (non-zero inset)
|
|
static bool logged_active = false;
|
|
if (!logged_active && (inset_x > 0.0 || inset_y > 0.0)) {
|
|
logged_active = true;
|
|
log_info("rawinput", "aspect compensation active: display {}x{}, native {}x{}, inset x={:.4f} y={:.4f}",
|
|
(int) DISPLAY_SIZE_X, (int) DISPLAY_SIZE_Y,
|
|
(int) DISPLAY_NATIVE_X, (int) DISPLAY_NATIVE_Y,
|
|
inset_x, inset_y);
|
|
}
|
|
|
|
// ignore touches inside the black bars (outside the displayed image)
|
|
if ((inset_x > 0.0 && (norm_x < inset_x || norm_x > 1.0 - inset_x)) ||
|
|
(inset_y > 0.0 && (norm_y < inset_y || norm_y > 1.0 - inset_y))) {
|
|
return false;
|
|
}
|
|
|
|
// remap the displayed image region back to the full 0.0 - 1.0 range
|
|
if (inset_x > 0.0) {
|
|
norm_x = (norm_x - inset_x) / (1.0 - 2.0 * inset_x);
|
|
}
|
|
if (inset_y > 0.0) {
|
|
norm_y = (norm_y - inset_y) / (1.0 - 2.0 * inset_y);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// determine the native (maximum) resolution advertised by the primary display device.
|
|
// used to detect and undo letterboxing when a non-native display mode is in use.
|
|
// falls back to the current display size if enumeration yields nothing larger.
|
|
static void detect_native_resolution() {
|
|
DISPLAY_NATIVE_X = DISPLAY_SIZE_X;
|
|
DISPLAY_NATIVE_Y = DISPLAY_SIZE_Y;
|
|
|
|
DEVMODE native_mode{};
|
|
native_mode.dmSize = sizeof(native_mode);
|
|
for (int i = 0; EnumDisplaySettings(nullptr, i, &native_mode); i++) {
|
|
if ((long) native_mode.dmPelsWidth * (long) native_mode.dmPelsHeight >
|
|
DISPLAY_NATIVE_X * DISPLAY_NATIVE_Y) {
|
|
DISPLAY_NATIVE_X = (long) native_mode.dmPelsWidth;
|
|
DISPLAY_NATIVE_Y = (long) native_mode.dmPelsHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool is_touchscreen(Device *device) {
|
|
|
|
// check if disabled
|
|
if (DISABLED) {
|
|
return false;
|
|
}
|
|
|
|
// check device type
|
|
if (device->type != HID) {
|
|
return false;
|
|
}
|
|
|
|
auto *hid = device->hidInfo;
|
|
auto &attributes = hid->attributes;
|
|
|
|
// filter by VID/PID
|
|
if (attributes.VendorID > 0) {
|
|
|
|
// P2418HT
|
|
// touch points apparently aren't released and will stay
|
|
if (attributes.VendorID == 0x1FD2 && attributes.ProductID == 0x6103) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ignore the "Touch Pad" (0x05) usage under the "Digitizers" (0x0d) usage page
|
|
if (hid->caps.UsagePage == 0x0d && hid->caps.Usage == 0x05) {
|
|
return false;
|
|
}
|
|
|
|
// check description
|
|
if (device->desc == "HID-compliant touch screen") {
|
|
// TODO: this only works on english OS (?)
|
|
return true;
|
|
}
|
|
|
|
// we can also check if there's touch point values inside
|
|
for (const auto &value_name : hid->value_caps_names) {
|
|
if (value_name == "Contact identifier") {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// nope this one probably not
|
|
return false;
|
|
}
|
|
|
|
void enable(Device *device) {
|
|
if (device->type == HID) {
|
|
device->hidInfo->touch.valid = true;
|
|
log_info("rawinput", "enabled touchscreen device: {} ({})", device->desc, device->name);
|
|
} else {
|
|
log_fatal("rawinput", "tried to enable touch functionality on non HID device");
|
|
}
|
|
}
|
|
|
|
void disable(Device *device) {
|
|
if (device->type == HID) {
|
|
device->hidInfo->touch.valid = false;
|
|
log_info("rawinput", "disabled touchscreen device: {} ({})", device->desc, device->name);
|
|
} else
|
|
log_fatal("rawinput", "tried to disable touch functionality on non HID device");
|
|
}
|
|
|
|
void update_input(Device *device) {
|
|
|
|
// check type
|
|
if (device->type != HID) {
|
|
log_warning("rawinput", "touch update called on non HID device");
|
|
return;
|
|
}
|
|
|
|
// get touch info
|
|
auto hid = device->hidInfo;
|
|
auto &touch = hid->touch;
|
|
if (!touch.valid) {
|
|
|
|
// not a registered touchscreen
|
|
return;
|
|
}
|
|
|
|
// parse elements
|
|
if (!touch.parsed_elements) {
|
|
log_info("rawinput", "parsing touch elements");
|
|
|
|
// clear lists first
|
|
touch.elements_contact_count.clear();
|
|
touch.elements_contact_identifier.clear();
|
|
touch.elements_x.clear();
|
|
touch.elements_y.clear();
|
|
touch.elements_width.clear();
|
|
touch.elements_height.clear();
|
|
touch.elements_pressed.clear();
|
|
touch.elements_pressure.clear();
|
|
|
|
// get value indices
|
|
for (int i = 0; i < (int) hid->value_caps_names.size(); i++) {
|
|
auto &name = hid->value_caps_names[i];
|
|
if (name == "Contact count") {
|
|
touch.elements_contact_count.push_back(i);
|
|
} else if (name == "X") {
|
|
touch.elements_x.push_back(i);
|
|
} else if (name == "Y") {
|
|
touch.elements_y.push_back(i);
|
|
} else if (name == "Width") {
|
|
touch.elements_width.push_back(i);
|
|
} else if (name == "Height") {
|
|
touch.elements_height.push_back(i);
|
|
} else if (name == "Contact identifier") {
|
|
touch.elements_contact_identifier.push_back(i);
|
|
} else if (name == "Pressure") {
|
|
touch.elements_pressure.push_back(i);
|
|
}
|
|
}
|
|
|
|
// get button indices
|
|
for (int i = 0; i < (int) hid->button_caps_names.size(); i++) {
|
|
auto &name = hid->button_caps_names[i];
|
|
if (name == "Tip Switch") {
|
|
touch.elements_pressed.push_back(i);
|
|
}
|
|
}
|
|
|
|
// check sizes
|
|
auto touch_size = touch.elements_contact_identifier.size();
|
|
if (touch_size != touch.elements_x.size() ||
|
|
touch_size != touch.elements_y.size() ||
|
|
touch_size != touch.elements_pressed.size())
|
|
{
|
|
log_info("rawinput", "touch element size mismatch: {}:contacts, {}:x, {}:y, {}:pressed",
|
|
touch.elements_contact_identifier.size(),
|
|
touch.elements_x.size(),
|
|
touch.elements_y.size(),
|
|
touch.elements_pressed.size());
|
|
disable(device);
|
|
return;
|
|
}
|
|
|
|
// mark as parsed
|
|
touch.parsed_elements = true;
|
|
log_info("rawinput", "touch elements parsed: {} status fields", touch.elements_x.size());
|
|
}
|
|
|
|
// check if display is initialized
|
|
if (!DISPLAY_INITIALIZED) {
|
|
update_current_display_mode();
|
|
}
|
|
|
|
// update timeouts here as well so events are in the right order
|
|
update_timeouts(device);
|
|
|
|
// determine the number of touches contained in this report, defaulting to the size of `elements_x`
|
|
size_t touch_report_count = touch.elements_x.size();
|
|
if (!touch.elements_contact_count.empty()) {
|
|
|
|
// support devices that have multiple "Contact count" fields, for some reason
|
|
size_t contact_count = 0;
|
|
for (auto &index : touch.elements_contact_count) {
|
|
contact_count = std::max(contact_count, (size_t) hid->value_states_raw[index]);
|
|
}
|
|
|
|
// https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-packet-reporting-modes
|
|
// hybrid mode devices will report a contact count of 0 for subsequent reports that are
|
|
// part of the same initial frame
|
|
// most laptops and touchscreens are like this - they have the contact count set to 5 but can actually report up to 10 fingers
|
|
if (contact_count > 0) {
|
|
if (contact_count > touch_report_count) {
|
|
touch.remaining_contact_count = contact_count - touch_report_count;
|
|
} else {
|
|
touch_report_count = contact_count;
|
|
touch.remaining_contact_count = 0;
|
|
}
|
|
} else if (touch.remaining_contact_count > 0) {
|
|
if (touch.remaining_contact_count > touch_report_count) {
|
|
touch.remaining_contact_count -= touch_report_count;
|
|
} else {
|
|
touch_report_count = touch.remaining_contact_count;
|
|
touch.remaining_contact_count = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// iterate all input data and get touch points
|
|
thread_local std::vector<HIDTouchPoint> touch_points;
|
|
touch_points.clear();
|
|
touch_points.reserve(touch.elements_x.size());
|
|
for (size_t i = 0; i < touch.elements_x.size(); i++) {
|
|
|
|
// if there are no more touch events to handle, exit out early
|
|
if (touch_report_count == 0) {
|
|
break;
|
|
}
|
|
touch_report_count--;
|
|
|
|
// build touch point
|
|
HIDTouchPoint hid_tp{};
|
|
auto pos_x = hid->value_states[touch.elements_x[i]];
|
|
auto pos_y = hid->value_states[touch.elements_y[i]];
|
|
switch (DISPLAY_ORIENTATION) {
|
|
case DMDO_DEFAULT:
|
|
hid_tp.x = pos_x;
|
|
hid_tp.y = pos_y;
|
|
break;
|
|
case DMDO_90:
|
|
hid_tp.x = 1.f - pos_y;
|
|
hid_tp.y = pos_x;
|
|
break;
|
|
case DMDO_180:
|
|
hid_tp.x = 1.f - pos_x;
|
|
hid_tp.y = 1.f - pos_y;
|
|
break;
|
|
case DMDO_270:
|
|
hid_tp.x = pos_y;
|
|
hid_tp.y = 1.f - pos_x;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// optionally invert
|
|
if (INVERTED) {
|
|
hid_tp.x = 1.f - hid_tp.x;
|
|
hid_tp.y = 1.f - hid_tp.y;
|
|
}
|
|
|
|
// generate ID (hopefully unique)
|
|
hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]];
|
|
hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);
|
|
|
|
//std::string src = "(none)";
|
|
|
|
// check if tip switch is down
|
|
size_t index_pressed = touch.elements_pressed[i];
|
|
for (auto &button_states : hid->button_states) {
|
|
if (index_pressed < button_states.size()) {
|
|
hid_tp.down = button_states[index_pressed];
|
|
|
|
/*
|
|
if (hid_tp.down)
|
|
src = "pressed (index: " + to_string(touch.elements_pressed[i]) + ", state: " + to_string(button_states[index_pressed]) +")";
|
|
*/
|
|
|
|
break;
|
|
} else
|
|
index_pressed -= button_states.size();
|
|
}
|
|
|
|
// check width/height of touch point to see if pressed
|
|
float width = 0.f, height = 0.f;
|
|
if (!hid_tp.down && i < touch.elements_width.size() && i < touch.elements_height.size()) {
|
|
width = hid->value_states[touch.elements_width[i]];
|
|
height = hid->value_states[touch.elements_height[i]];
|
|
hid_tp.down = width > 0.f && height > 0.f;
|
|
|
|
/*
|
|
if (hid_tp.down)
|
|
src = "width_height (width index: " + to_string(touch.elements_width[i]) + ", height index: " + to_string(touch.elements_height[i]) + ")";
|
|
*/
|
|
}
|
|
|
|
// so last thing we can check is the pressure
|
|
if (!hid_tp.down && i < touch.elements_pressure.size()) {
|
|
auto pressure = hid->value_states[touch.elements_pressure[i]];
|
|
hid_tp.down = pressure > 0.f;
|
|
|
|
/*
|
|
if (hid_tp.down)
|
|
src = "pressure (index: " + to_string(touch.elements_pressure[i]) + ")";
|
|
*/
|
|
}
|
|
|
|
/*
|
|
log_info("rawinput",
|
|
"touch i: " + to_string(i) +
|
|
" (id: " + to_string(hid_tp.id) +
|
|
"), ci: " + to_string(hid->value_states_raw[touch.elements_contact_identifier[i]]) +
|
|
", x: " + to_string(pos_x) +
|
|
", y: " + to_string(pos_y) +
|
|
", width: " + to_string(width) +
|
|
", height: " + to_string(height) +
|
|
", down: " + to_string(hid_tp.down) +
|
|
", src: " + src);
|
|
*/
|
|
|
|
// add to touch points
|
|
touch_points.emplace_back(hid_tp);
|
|
}
|
|
|
|
// process touch points
|
|
thread_local std::vector<DWORD> touch_removes;
|
|
thread_local std::vector<TouchPoint> touch_writes;
|
|
thread_local std::vector<DWORD> touch_modifications;
|
|
touch_removes.clear();
|
|
touch_writes.clear();
|
|
touch_modifications.clear();
|
|
touch_removes.reserve(touch.touch_points.size() + touch_points.size());
|
|
touch_writes.reserve(touch_points.size());
|
|
touch_modifications.reserve(touch_points.size());
|
|
|
|
// drop every touch point with the given id (marking it as released)
|
|
auto remove_touch_point = [&] (DWORD id) {
|
|
touch_removes.push_back(id);
|
|
touch.touch_points.erase(std::remove_if(
|
|
touch.touch_points.begin(),
|
|
touch.touch_points.end(),
|
|
[id] (const HIDTouchPoint &x) {
|
|
return x.id == id;
|
|
}), touch.touch_points.end());
|
|
};
|
|
|
|
for (auto &hid_tp : touch_points) {
|
|
|
|
// check if existing
|
|
auto existing = std::find_if(
|
|
touch.touch_points.begin(),
|
|
touch.touch_points.end(),
|
|
[hid_tp] (const HIDTouchPoint &x) {
|
|
return x.id == hid_tp.id;
|
|
});
|
|
|
|
/*
|
|
ssize_t ttl = -1;
|
|
int64_t last_report = -1;
|
|
if (existing != touch.touch_points.end()) {
|
|
ttl = existing->ttl;
|
|
last_report = existing->last_report;
|
|
}
|
|
|
|
log_info("rawinput",
|
|
"id: " + to_string(hid_tp.id) +
|
|
", existing: " + to_string(existing != touch.touch_points.end()) +
|
|
", ttl: " + to_string(ttl) +
|
|
", last_report: " + to_string(last_report) +
|
|
", down: " + to_string(hid_tp.down));
|
|
*/
|
|
|
|
// check if pressed
|
|
if (!hid_tp.down) {
|
|
|
|
// only remove if it exists
|
|
if (existing != touch.touch_points.end()) {
|
|
remove_touch_point(hid_tp.id);
|
|
}
|
|
} else {
|
|
|
|
// start from the normalized (0.0 - 1.0) digitizer coordinates
|
|
double norm_x = hid_tp.x;
|
|
double norm_y = hid_tp.y;
|
|
|
|
// undo letterboxing so raw input touches line up with the displayed
|
|
// image; touches that land in the black bars are treated as released
|
|
if (aspect_compensation_enabled() && !remap_aspect_compensation(norm_x, norm_y)) {
|
|
if (existing != touch.touch_points.end()) {
|
|
remove_touch_point(hid_tp.id);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// scale the normalized coordinates to the monitor to obtain the
|
|
// physical screen position of the touch
|
|
TouchPoint tp {
|
|
.id = hid_tp.id,
|
|
.x = (long) (norm_x * DISPLAY_SIZE_X),
|
|
.y = (long) (norm_y * DISPLAY_SIZE_Y),
|
|
.mouse = false,
|
|
};
|
|
touch_writes.push_back(tp);
|
|
touch_modifications.push_back(hid_tp.id);
|
|
|
|
// check if existing
|
|
if (existing == touch.touch_points.end()) {
|
|
|
|
// add new touch point
|
|
touch.touch_points.push_back(hid_tp);
|
|
|
|
} else {
|
|
|
|
// update existing touch point
|
|
*existing = hid_tp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set TTL and last report time
|
|
if (!touch.elements_x.empty()) {
|
|
|
|
// a HID digitizer reports only a fixed number of contacts per report and
|
|
// cycles the remaining ones through following reports, so a contact can be
|
|
// absent for up to this many reports before it comes back around
|
|
auto ttl = touch.touch_points.size() / touch.elements_x.size() + 1;
|
|
auto system_time_ms = get_system_milliseconds();
|
|
|
|
// extend every tracked contact to the current cycle length without ever
|
|
// shrinking it; otherwise a rising contact count (e.g. rapid taps) lengthens
|
|
// the cycle and can starve a stationary held contact into being reaped before
|
|
// it is reported again. the 50ms last-report timeout remains the hard backstop
|
|
for (auto &hid_tp : touch.touch_points) {
|
|
if (hid_tp.ttl < ttl) {
|
|
hid_tp.ttl = ttl;
|
|
}
|
|
}
|
|
|
|
// only contacts actually seen this report refresh their last-report time
|
|
for (auto &hid_tp_id : touch_modifications) {
|
|
for (auto &hid_tp : touch.touch_points) {
|
|
if (hid_tp.id == hid_tp_id) {
|
|
hid_tp.last_report = system_time_ms;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove dead touch points
|
|
auto ttl_it = touch.touch_points.begin();
|
|
while (ttl_it != touch.touch_points.end()) {
|
|
auto &tp = *ttl_it;
|
|
if (tp.ttl == 0) {
|
|
touch_removes.push_back(tp.id);
|
|
ttl_it = touch.touch_points.erase(ttl_it);
|
|
} else {
|
|
tp.ttl--;
|
|
ttl_it++;
|
|
}
|
|
}
|
|
|
|
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
|
|
|
// update touch module
|
|
touch_remove_points(&touch_removes);
|
|
touch_write_points(&touch_writes);
|
|
#endif
|
|
}
|
|
|
|
void update_timeouts(Device *device) {
|
|
|
|
// check type
|
|
if (device->type != HID) {
|
|
log_fatal("rawinput", "touch timeout update called on non HID device");
|
|
return;
|
|
}
|
|
|
|
// get touch info
|
|
auto hid = device->hidInfo;
|
|
auto &touch = hid->touch;
|
|
if (!touch.valid) {
|
|
return; // not a registered touchscreen
|
|
}
|
|
|
|
// calculate deadline - we allow the devices 50ms of not sending shit
|
|
auto deadline = get_system_milliseconds() - 50;
|
|
|
|
// check touch points
|
|
thread_local std::vector<DWORD> touch_removes;
|
|
touch_removes.clear();
|
|
touch_removes.reserve(touch.touch_points.size());
|
|
auto touch_it = touch.touch_points.begin();
|
|
while (touch_it != touch.touch_points.end()) {
|
|
auto &hid_tp = *touch_it;
|
|
|
|
// see if it's behind the deadline
|
|
if (hid_tp.last_report < deadline) {
|
|
|
|
// oops it's gone
|
|
touch_removes.push_back(hid_tp.id);
|
|
touch_it = touch.touch_points.erase(touch_it);
|
|
|
|
} else {
|
|
|
|
// check the next device
|
|
touch_it++;
|
|
}
|
|
}
|
|
|
|
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
|
|
|
// remove from touch module
|
|
touch_remove_points(&touch_removes);
|
|
#endif
|
|
}
|
|
|
|
void update_timeouts(RawInputManager *manager) {
|
|
|
|
// iterate all devices
|
|
for (auto &device : manager->devices_get()) {
|
|
|
|
// check if it's a valid touchscreen
|
|
if (device.type == HID && device.hidInfo->touch.valid) {
|
|
|
|
// lock n' load
|
|
device.mutex->lock();
|
|
update_timeouts(&device);
|
|
device.mutex->unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool is_enabled(RawInputManager *manager) {
|
|
|
|
// check if disabled or manager is null
|
|
if (DISABLED || manager == nullptr)
|
|
return false;
|
|
|
|
// check if at least one device is marked as valid touchscreen
|
|
for (auto &device : manager->devices_get()) {
|
|
if (device.type == HID && device.hidInfo->touch.valid) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// no valid touch screen found
|
|
return false;
|
|
}
|
|
|
|
static void update_current_display_mode() {
|
|
// determine monitor size
|
|
static RECT display_rect;
|
|
GetWindowRect(GetDesktopWindow(), &display_rect);
|
|
DISPLAY_SIZE_X = display_rect.right - display_rect.left;
|
|
DISPLAY_SIZE_Y = display_rect.bottom - display_rect.top;
|
|
log_info("rawinput", "primary display size: {}x{}", (int) DISPLAY_SIZE_X, (int) DISPLAY_SIZE_Y);
|
|
|
|
// determine monitor orientation
|
|
DEVMODE display_mode{};
|
|
display_mode.dmSize = sizeof(DEVMODE);
|
|
if (!EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &display_mode, EDS_RAWMODE)) {
|
|
log_info("rawinput", "failed to determine monitor mode");
|
|
} else if (display_mode.dmFields & DM_DISPLAYORIENTATION) {
|
|
DISPLAY_ORIENTATION = display_mode.dmDisplayOrientation;
|
|
switch (DISPLAY_ORIENTATION) {
|
|
case DMDO_DEFAULT:
|
|
log_info("rawinput", "display rotation: 0");
|
|
break;
|
|
case DMDO_90:
|
|
log_info("rawinput", "display rotation: 90");
|
|
break;
|
|
case DMDO_180:
|
|
log_info("rawinput", "display rotation: 180");
|
|
break;
|
|
case DMDO_270:
|
|
log_info("rawinput", "display rotation: 270");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// another XP fix
|
|
if (!IsWindowsVistaOrGreater()) {
|
|
switch (DISPLAY_ORIENTATION) {
|
|
case DMDO_90:
|
|
DISPLAY_ORIENTATION = DMDO_180;
|
|
log_info("rawinput", "flipping to 180");
|
|
break;
|
|
case DMDO_270:
|
|
DISPLAY_ORIENTATION = DMDO_90;
|
|
log_info("rawinput", "flipping to 90");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
log_info("rawinput", "failed to determine monitor orientation");
|
|
}
|
|
|
|
// mark as initialized
|
|
DISPLAY_INITIALIZED = true;
|
|
}
|
|
|
|
void display_update() {
|
|
|
|
// check if disabled
|
|
if (DISABLED)
|
|
return;
|
|
|
|
update_current_display_mode();
|
|
|
|
if (aspect_compensation_enabled()) {
|
|
// determine the native (maximum) resolution of the primary display; used to
|
|
// detect and undo letterboxing when a non-native display mode is in use
|
|
detect_native_resolution();
|
|
log_info("rawinput", "primary display native size: {}x{}",
|
|
(int) DISPLAY_NATIVE_X, (int) DISPLAY_NATIVE_Y);
|
|
}
|
|
}
|
|
}
|