1 // Copyright 2019 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "Core/HW/WiimoteEmu/Extension/UDrawTablet.h"
6
7 #include <array>
8 #include <cassert>
9
10 #include "Common/BitUtils.h"
11 #include "Common/Common.h"
12 #include "Common/CommonTypes.h"
13 #include "Core/HW/WiimoteEmu/WiimoteEmu.h"
14
15 #include "InputCommon/ControllerEmu/Control/Input.h"
16 #include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
17 #include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
18 #include "InputCommon/ControllerEmu/ControlGroup/Triggers.h"
19
20 namespace WiimoteEmu
21 {
22 constexpr std::array<u8, 6> udraw_tablet_id{{0xff, 0x00, 0xa4, 0x20, 0x01, 0x12}};
23
24 constexpr std::array<u8, 2> udraw_tablet_button_bitmasks{{
25 UDrawTablet::BUTTON_ROCKER_UP,
26 UDrawTablet::BUTTON_ROCKER_DOWN,
27 }};
28
29 constexpr std::array<const char*, 2> udraw_tablet_button_names{{
30 _trans("Rocker Up"),
31 _trans("Rocker Down"),
32 }};
33
UDrawTablet()34 UDrawTablet::UDrawTablet() : Extension3rdParty("uDraw", _trans("uDraw GameTablet"))
35 {
36 // Buttons
37 groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
38 for (auto& button_name : udraw_tablet_button_names)
39 {
40 m_buttons->AddInput(ControllerEmu::Translate, button_name);
41 }
42
43 // Stylus
44 groups.emplace_back(m_stylus = new ControllerEmu::AnalogStick(
45 _trans("Stylus"), std::make_unique<ControllerEmu::SquareStickGate>(1.0)));
46
47 // Touch
48 groups.emplace_back(m_touch = new ControllerEmu::Triggers(_trans("Touch")));
49 m_touch->AddInput(ControllerEmu::Translate, _trans("Pressure"));
50 }
51
Update()52 void UDrawTablet::Update()
53 {
54 DataFormat tablet_data = {};
55
56 // Pressure:
57 // Min/Max values produced on my device: 0x08, 0xf2
58 // We're just gonna assume it's an old sensor and use the full byte range:
59 // Note: Pressure values are valid even when stylus is lifted.
60 constexpr u8 max_pressure = 0xff;
61
62 const auto touch_state = m_touch->GetState();
63 tablet_data.pressure = static_cast<u8>(touch_state.data[0] * max_pressure);
64
65 // Stylus X/Y:
66 // Min/Max X values (when touched) produced on my device: 0x4f, 0x7B3
67 // Drawing area edge (approx) X values on my device: 0x56, 0x7a5
68 // Min/Max Y values (when touched) produced on my device: 0x53, 0x5b4
69 // Drawing area edge (approx) Y values on my device: 0x5e, 0x5a5
70
71 // Calibrated for "uDraw Studio: Instant Artist".
72 constexpr u16 min_x = 0x56;
73 constexpr u16 max_x = 0x780;
74 constexpr u16 min_y = 0x65;
75 constexpr u16 max_y = 0x5a5;
76 constexpr double center_x = (max_x + min_x) / 2.0;
77 constexpr double center_y = (max_y + min_y) / 2.0;
78
79 // Neutral (lifted) stylus state:
80 u16 stylus_x = 0x7ff;
81 u16 stylus_y = 0x7ff;
82
83 // TODO: Expose the lifted stylus state in the UI.
84 bool is_stylus_lifted = false;
85
86 const auto stylus_state = m_stylus->GetState();
87
88 if (!is_stylus_lifted)
89 {
90 stylus_x = u16(center_x + stylus_state.x * (max_x - center_x));
91 stylus_y = u16(center_y + stylus_state.y * (max_y - center_y));
92 }
93
94 tablet_data.stylus_x1 = stylus_x & 0xff;
95 tablet_data.stylus_x2 = stylus_x >> 8;
96 tablet_data.stylus_y1 = stylus_y & 0xff;
97 tablet_data.stylus_y2 = stylus_y >> 8;
98
99 // Buttons:
100 m_buttons->GetState(&tablet_data.buttons, udraw_tablet_button_bitmasks.data());
101
102 // Flip button bits
103 constexpr u8 buttons_neutral_state = 0xfb;
104 tablet_data.buttons ^= buttons_neutral_state;
105
106 // Always 0xff
107 tablet_data.unk = 0xff;
108
109 Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = tablet_data;
110 }
111
Reset()112 void UDrawTablet::Reset()
113 {
114 EncryptedExtension::Reset();
115
116 m_reg.identifier = udraw_tablet_id;
117
118 // Both 0x20 and 0x30 calibration sections are just filled with 0xff on the real tablet:
119 m_reg.calibration.fill(0xff);
120 }
121
GetGroup(UDrawTabletGroup group)122 ControllerEmu::ControlGroup* UDrawTablet::GetGroup(UDrawTabletGroup group)
123 {
124 switch (group)
125 {
126 case UDrawTabletGroup::Buttons:
127 return m_buttons;
128 case UDrawTabletGroup::Stylus:
129 return m_stylus;
130 case UDrawTabletGroup::Touch:
131 return m_touch;
132 default:
133 assert(false);
134 return nullptr;
135 }
136 }
137
138 } // namespace WiimoteEmu
139