InputConfigDiagBitmaps.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // Copyright 2010 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include <memory>
  6. #include <mutex>
  7. #include <string>
  8. #include <vector>
  9. #include <wx/bitmap.h>
  10. #include <wx/brush.h>
  11. #include <wx/colour.h>
  12. #include <wx/dcmemory.h>
  13. #include <wx/font.h>
  14. #include <wx/notebook.h>
  15. #include <wx/pen.h>
  16. #include <wx/statbmp.h>
  17. #include "DolphinWX/InputConfigDiag.h"
  18. #include "DolphinWX/WxUtils.h"
  19. #include "InputCommon/ControllerEmu.h"
  20. #include "InputCommon/ControllerInterface/ControllerInterface.h"
  21. #include "InputCommon/ControllerInterface/Device.h"
  22. struct ShapePosition
  23. {
  24. double max;
  25. double diag;
  26. double box;
  27. double scale;
  28. double dz;
  29. double range;
  30. wxCoord offset;
  31. };
  32. // regular octagon
  33. static void DrawOctagon(wxDC* dc, ShapePosition p)
  34. {
  35. const int vertices = 8;
  36. double radius = p.max;
  37. wxPoint point[vertices];
  38. double angle = 2.0 * M_PI / vertices;
  39. for (int i = 0; i < vertices; i++)
  40. {
  41. double a = (angle * i);
  42. double x = radius * cos(a);
  43. double y = radius * sin(a);
  44. point[i].x = x;
  45. point[i].y = y;
  46. }
  47. dc->DrawPolygon(vertices, point, p.offset, p.offset);
  48. }
  49. // irregular dodecagon
  50. static void DrawDodecagon(wxDC* dc, ShapePosition p)
  51. {
  52. const int vertices = 12;
  53. wxPoint point[vertices];
  54. point[0].x = p.dz; point[0].y = p.max;
  55. point[1].x = p.diag; point[1].y = p.diag;
  56. point[2].x = p.max; point[2].y = p.dz;
  57. point[3].x = p.max; point[3].y = -p.dz;
  58. point[4].x = p.diag; point[4].y = -p.diag;
  59. point[5].x = p.dz; point[5].y = -p.max;
  60. point[6].x = -p.dz; point[6].y = -p.max;
  61. point[7].x = -p.diag; point[7].y = -p.diag;
  62. point[8].x = -p.max; point[8].y = -p.dz;
  63. point[9].x = -p.max; point[9].y = p.dz;
  64. point[10].x = -p.diag; point[10].y = p.diag;
  65. point[11].x = -p.dz; point[11].y = p.max;
  66. dc->DrawPolygon(vertices, point, p.offset, p.offset);
  67. }
  68. static void DrawCenteredRectangle(wxDC &dc, int x, int y, int w, int h)
  69. {
  70. x -= w / 2;
  71. y -= h / 2;
  72. dc.DrawRectangle(x, y, w, h);
  73. }
  74. #define VIS_BITMAP_SIZE 64
  75. #define VIS_NORMALIZE(a) ((a / 2.0) + 0.5)
  76. #define VIS_COORD(a) ((VIS_NORMALIZE(a)) * VIS_BITMAP_SIZE)
  77. #define COORD_VIS_SIZE 4
  78. static void DrawCoordinate(wxDC &dc, ControlState x, ControlState y)
  79. {
  80. int xc = VIS_COORD(x);
  81. int yc = VIS_COORD(y);
  82. DrawCenteredRectangle(dc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE);
  83. }
  84. static void DrawButton(unsigned int* const bitmasks, unsigned int buttons, unsigned int n, wxDC& dc, ControlGroupBox* g, unsigned int row)
  85. {
  86. if (buttons & bitmasks[(row * 8) + n])
  87. {
  88. dc.SetBrush(*wxRED_BRUSH);
  89. }
  90. else
  91. {
  92. unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128;
  93. dc.SetBrush(wxBrush(wxColour(amt, amt, amt)));
  94. }
  95. dc.DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12);
  96. // text
  97. const std::string name = g->control_group->controls[(row * 8) + n]->name;
  98. // bit of hax so ZL, ZR show up as L, R
  99. dc.DrawText(StrToWxStr(std::string(1, (name[1] && name[1] < 'a') ? name[1] : name[0])), n * 12 + 2, 1 + ((row == 0) ? 0 : (row * 11)));
  100. }
  101. static void DrawControlGroupBox(wxDC &dc, ControlGroupBox *g)
  102. {
  103. switch (g->control_group->type)
  104. {
  105. case GROUP_TYPE_TILT :
  106. case GROUP_TYPE_STICK :
  107. case GROUP_TYPE_CURSOR :
  108. {
  109. // this is starting to be a mess combining all these in one case
  110. ControlState x = 0, y = 0, z = 0;
  111. switch (g->control_group->type)
  112. {
  113. case GROUP_TYPE_STICK :
  114. ((ControllerEmu::AnalogStick*)g->control_group)->GetState(&x, &y);
  115. break;
  116. case GROUP_TYPE_TILT :
  117. ((ControllerEmu::Tilt*)g->control_group)->GetState(&x, &y);
  118. break;
  119. case GROUP_TYPE_CURSOR :
  120. ((ControllerEmu::Cursor*)g->control_group)->GetState(&x, &y, &z);
  121. break;
  122. }
  123. // ir cursor forward movement
  124. if (GROUP_TYPE_CURSOR == g->control_group->type)
  125. {
  126. if (z)
  127. {
  128. dc.SetPen(*wxRED_PEN);
  129. dc.SetBrush(*wxRED_BRUSH);
  130. }
  131. else
  132. {
  133. dc.SetPen(*wxGREY_PEN);
  134. dc.SetBrush(*wxGREY_BRUSH);
  135. }
  136. dc.DrawRectangle(0, 31 - z*31, 64, 2);
  137. }
  138. // input zone
  139. dc.SetPen(*wxLIGHT_GREY_PEN);
  140. dc.SetBrush(*wxWHITE_BRUSH);
  141. if (GROUP_TYPE_STICK == g->control_group->type)
  142. {
  143. // outline and fill colors
  144. wxBrush LightGrayBrush("#dddddd");
  145. wxPen LightGrayPen("#bfbfbf");
  146. dc.SetBrush(LightGrayBrush);
  147. dc.SetPen(LightGrayPen);
  148. ShapePosition p;
  149. p.box = 64;
  150. p.offset = p.box / 2;
  151. p.range = 256;
  152. p.scale = p.box / p.range;
  153. p.dz = 15 * p.scale;
  154. bool octagon = false;
  155. if (g->control_group->name == "Main Stick")
  156. {
  157. p.max = 87 * p.scale;
  158. p.diag = 55 * p.scale;
  159. }
  160. else if (g->control_group->name == "C-Stick")
  161. {
  162. p.max = 74 * p.scale;
  163. p.diag = 46 * p.scale;
  164. }
  165. else
  166. {
  167. p.scale = 1;
  168. p.max = 32;
  169. octagon = true;
  170. }
  171. if (octagon)
  172. DrawOctagon(&dc, p);
  173. else
  174. DrawDodecagon(&dc, p);
  175. }
  176. else
  177. {
  178. dc.DrawRectangle(16, 16, 32, 32);
  179. }
  180. if (GROUP_TYPE_CURSOR != g->control_group->type)
  181. {
  182. // deadzone circle
  183. dc.SetBrush(*wxLIGHT_GREY_BRUSH);
  184. dc.DrawCircle(32, 32, g->control_group->settings[SETTING_DEADZONE]->value * 32);
  185. }
  186. // raw dot
  187. {
  188. ControlState xx, yy;
  189. xx = g->control_group->controls[3]->control_ref->State();
  190. xx -= g->control_group->controls[2]->control_ref->State();
  191. yy = g->control_group->controls[1]->control_ref->State();
  192. yy -= g->control_group->controls[0]->control_ref->State();
  193. dc.SetPen(*wxGREY_PEN);
  194. dc.SetBrush(*wxGREY_BRUSH);
  195. DrawCoordinate(dc, xx, yy);
  196. }
  197. // adjusted dot
  198. if (x != 0 || y != 0)
  199. {
  200. dc.SetPen(*wxRED_PEN);
  201. dc.SetBrush(*wxRED_BRUSH);
  202. // XXX: The adjusted values flip the Y axis to be in the format
  203. // the Wii expects. Should this be in WiimoteEmu.cpp instead?
  204. DrawCoordinate(dc, x, -y);
  205. }
  206. }
  207. break;
  208. case GROUP_TYPE_FORCE :
  209. {
  210. ControlState raw_dot[3];
  211. ControlState adj_dot[3];
  212. const ControlState deadzone = g->control_group->settings[0]->value;
  213. // adjusted
  214. ((ControllerEmu::Force*)g->control_group)->GetState(adj_dot);
  215. // raw
  216. for (unsigned int i=0; i<3; ++i)
  217. {
  218. raw_dot[i] = (g->control_group->controls[i*2 + 1]->control_ref->State() -
  219. g->control_group->controls[i*2]->control_ref->State());
  220. }
  221. // deadzone rect for forward/backward visual
  222. dc.SetBrush(*wxLIGHT_GREY_BRUSH);
  223. dc.SetPen(*wxLIGHT_GREY_PEN);
  224. int deadzone_height = deadzone * VIS_BITMAP_SIZE;
  225. DrawCenteredRectangle(dc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height);
  226. #define LINE_HEIGHT 2
  227. int line_y;
  228. // raw forward/background line
  229. dc.SetPen(*wxGREY_PEN);
  230. dc.SetBrush(*wxGREY_BRUSH);
  231. line_y = VIS_COORD(raw_dot[2]);
  232. DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
  233. // adjusted forward/background line
  234. if (adj_dot[2] != 0.0)
  235. {
  236. dc.SetPen(*wxRED_PEN);
  237. dc.SetBrush(*wxRED_BRUSH);
  238. line_y = VIS_COORD(adj_dot[2]);
  239. DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
  240. }
  241. #define DEADZONE_RECT_SIZE 32
  242. // empty deadzone square
  243. dc.SetBrush(*wxWHITE_BRUSH);
  244. dc.SetPen(*wxLIGHT_GREY_PEN);
  245. DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE, DEADZONE_RECT_SIZE);
  246. // deadzone square
  247. dc.SetBrush(*wxLIGHT_GREY_BRUSH);
  248. int dz_size = (deadzone * DEADZONE_RECT_SIZE);
  249. DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size);
  250. // raw dot
  251. dc.SetPen(*wxGREY_PEN);
  252. dc.SetBrush(*wxGREY_BRUSH);
  253. DrawCoordinate(dc, raw_dot[1], raw_dot[0]);
  254. // adjusted dot
  255. if (adj_dot[1] != 0 && adj_dot[0] != 0)
  256. {
  257. dc.SetPen(*wxRED_PEN);
  258. dc.SetBrush(*wxRED_BRUSH);
  259. DrawCoordinate(dc, adj_dot[1], adj_dot[0]);
  260. }
  261. }
  262. break;
  263. case GROUP_TYPE_BUTTONS :
  264. {
  265. unsigned int button_count = ((unsigned int)g->control_group->controls.size());
  266. // draw the shit
  267. dc.SetPen(*wxGREY_PEN);
  268. unsigned int* const bitmasks = new unsigned int[button_count];
  269. for (unsigned int n = 0; n<button_count; ++n)
  270. bitmasks[n] = (1 << n);
  271. unsigned int buttons = 0;
  272. ((ControllerEmu::Buttons*)g->control_group)->GetState(&buttons, bitmasks);
  273. // Draw buttons in rows of 8
  274. for (unsigned int row = 0; row < ceil((float)button_count / 8.0f); row++)
  275. {
  276. unsigned int buttons_to_draw = 8;
  277. if ((button_count - row * 8) <= 8)
  278. buttons_to_draw = button_count - row * 8;
  279. for (unsigned int n = 0; n < buttons_to_draw; ++n)
  280. {
  281. DrawButton(bitmasks, buttons, n, dc, g, row);
  282. }
  283. }
  284. delete[] bitmasks;
  285. }
  286. break;
  287. case GROUP_TYPE_TRIGGERS :
  288. {
  289. const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size()));
  290. // draw the shit
  291. dc.SetPen(*wxGREY_PEN);
  292. ControlState deadzone = g->control_group->settings[0]->value;
  293. ControlState* const trigs = new ControlState[trigger_count];
  294. ((ControllerEmu::Triggers*)g->control_group)->GetState(trigs);
  295. for (unsigned int n = 0; n < trigger_count; ++n)
  296. {
  297. ControlState trig_r = g->control_group->controls[n]->control_ref->State();
  298. // outline
  299. dc.SetPen(*wxGREY_PEN);
  300. dc.SetBrush(*wxWHITE_BRUSH);
  301. dc.DrawRectangle(0, n*12, 64, 14);
  302. // raw
  303. dc.SetBrush(*wxGREY_BRUSH);
  304. dc.DrawRectangle(0, n*12, trig_r*64, 14);
  305. // deadzone affected
  306. dc.SetBrush(*wxRED_BRUSH);
  307. dc.DrawRectangle(0, n*12, trigs[n]*64, 14);
  308. // text
  309. dc.DrawText(StrToWxStr(g->control_group->controls[n]->name), 3, n*12 + 1);
  310. }
  311. delete[] trigs;
  312. // deadzone box
  313. dc.SetPen(*wxLIGHT_GREY_PEN);
  314. dc.SetBrush(*wxTRANSPARENT_BRUSH);
  315. dc.DrawRectangle(0, 0, deadzone*64, trigger_count*14);
  316. }
  317. break;
  318. case GROUP_TYPE_MIXED_TRIGGERS :
  319. {
  320. const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size() / 2));
  321. // draw the shit
  322. dc.SetPen(*wxGREY_PEN);
  323. ControlState thresh = g->control_group->settings[0]->value;
  324. for (unsigned int n = 0; n < trigger_count; ++n)
  325. {
  326. dc.SetBrush(*wxRED_BRUSH);
  327. ControlState trig_d = g->control_group->controls[n]->control_ref->State();
  328. ControlState trig_a = trig_d > thresh ? 1
  329. : g->control_group->controls[n+trigger_count]->control_ref->State();
  330. dc.DrawRectangle(0, n*12, 64+20, 14);
  331. if (trig_d <= thresh)
  332. dc.SetBrush(*wxWHITE_BRUSH);
  333. dc.DrawRectangle(trig_a*64, n*12, 64+20, 14);
  334. dc.DrawRectangle(64, n*12, 32, 14);
  335. // text
  336. dc.DrawText(StrToWxStr(g->control_group->controls[n+trigger_count]->name), 3, n*12 + 1);
  337. dc.DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])), 64 + 3, n*12 + 1);
  338. }
  339. // threshold box
  340. dc.SetPen(*wxLIGHT_GREY_PEN);
  341. dc.SetBrush(*wxTRANSPARENT_BRUSH);
  342. dc.DrawRectangle(thresh*64, 0, 128, trigger_count*14);
  343. }
  344. break;
  345. case GROUP_TYPE_SLIDER:
  346. {
  347. const ControlState deadzone = g->control_group->settings[0]->value;
  348. ControlState state = g->control_group->controls[1]->control_ref->State() - g->control_group->controls[0]->control_ref->State();
  349. dc.SetPen(*wxGREY_PEN);
  350. dc.SetBrush(*wxGREY_BRUSH);
  351. dc.DrawRectangle(31 + state * 30, 0, 2, 14);
  352. ControlState adj_state;
  353. ((ControllerEmu::Slider*)g->control_group)->GetState(&adj_state);
  354. if (state)
  355. {
  356. dc.SetPen(*wxRED_PEN);
  357. dc.SetBrush(*wxRED_BRUSH);
  358. dc.DrawRectangle(31 + adj_state * 30, 0, 2, 14);
  359. }
  360. // deadzone box
  361. dc.SetPen(*wxLIGHT_GREY_PEN);
  362. dc.SetBrush(*wxTRANSPARENT_BRUSH);
  363. dc.DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14);
  364. }
  365. break;
  366. default:
  367. break;
  368. }
  369. }
  370. void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
  371. {
  372. wxFont small_font(6, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
  373. g_controller_interface.UpdateInput();
  374. GamepadPage* const current_page = (GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection());
  375. for (ControlGroupBox* g : current_page->control_groups)
  376. {
  377. // if this control group has a bitmap
  378. if (g->static_bitmap)
  379. {
  380. wxMemoryDC dc;
  381. wxBitmap bitmap(g->static_bitmap->GetBitmap());
  382. dc.SelectObject(bitmap);
  383. dc.Clear();
  384. dc.SetFont(small_font);
  385. dc.SetTextForeground(0xC0C0C0);
  386. DrawControlGroupBox(dc, g);
  387. // box outline
  388. // Windows XP color
  389. dc.SetPen(wxPen("#7f9db9"));
  390. dc.SetBrush(*wxTRANSPARENT_BRUSH);
  391. dc.DrawRectangle(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
  392. // label for sticks and stuff
  393. if (64 == bitmap.GetHeight())
  394. dc.DrawText(StrToWxStr(g->control_group->name).Upper(), 4, 2);
  395. dc.SelectObject(wxNullBitmap);
  396. g->static_bitmap->SetBitmap(bitmap);
  397. }
  398. }
  399. }