coleco.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #include <furi.h>
  2. #include <furi_hal_gpio.h>
  3. #include <gui/gui.h>
  4. #include <gui/modules/button_panel.h>
  5. const GpioPin* const pin_up = &gpio_ext_pa4;
  6. const GpioPin* const pin_down = &gpio_ext_pb2;
  7. const GpioPin* const pin_right = &gpio_ext_pa7;
  8. const GpioPin* const pin_left = &gpio_ext_pa6;
  9. const GpioPin* const pin_fire = &gpio_ext_pc3;
  10. const GpioPin* const pin_mode = &gpio_ext_pc1;
  11. typedef enum
  12. {
  13. EventTypeTick,
  14. EventTypeKey,
  15. } EventType;
  16. typedef struct
  17. {
  18. EventType type;
  19. InputEvent input;
  20. } PluginEvent;
  21. typedef enum
  22. {
  23. WorkerEvtStop = (1 << 0),
  24. WorkerEvtModeChange = (1 << 1),
  25. WorkerEvtUpChange = (1 << 2),
  26. WorkerEvtDownChange = (1 << 3),
  27. WorkerEvtRightChange = (1 << 4),
  28. WorkerEvtLeftChange = (1 << 5),
  29. WorkerEvtCodeChange = (1 << 6),
  30. } WorkerEvtFlags;
  31. typedef struct
  32. {
  33. FuriThread* thread;
  34. volatile bool mode;
  35. bool button_up;
  36. bool button_down;
  37. bool button_right;
  38. bool button_left;
  39. bool button_fire;
  40. unsigned int button_code;
  41. ButtonPanel* button_panel;
  42. bool dpad;
  43. } Coleco;
  44. static void mode_isr(void* context)
  45. {
  46. Coleco* coleco = (Coleco*)context;
  47. coleco->mode = furi_hal_gpio_read(&gpio_ext_pc1);
  48. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtModeChange);
  49. }
  50. static int32_t coleco_output_worker(void* context)
  51. {
  52. Coleco* coleco = (Coleco*)context;
  53. furi_hal_gpio_add_int_callback(pin_mode, mode_isr, coleco);
  54. while(1)
  55. {
  56. uint32_t events = furi_thread_flags_wait(0x7F, FuriFlagWaitAny, FuriWaitForever);
  57. furi_check((events & FuriFlagError) == 0);
  58. if (events & WorkerEvtStop)
  59. {
  60. break;
  61. }
  62. if (events & WorkerEvtModeChange)
  63. {
  64. if (coleco->mode)
  65. {
  66. furi_hal_gpio_write(pin_up, !coleco->button_up);
  67. furi_hal_gpio_write(pin_down, !coleco->button_down);
  68. furi_hal_gpio_write(pin_right, !coleco->button_right);
  69. furi_hal_gpio_write(pin_left, !coleco->button_left);
  70. }
  71. else
  72. {
  73. furi_hal_gpio_write(pin_left, (coleco->button_code & 1));
  74. furi_hal_gpio_write(pin_down, (coleco->button_code & 2));
  75. furi_hal_gpio_write(pin_right, (coleco->button_code & 4));
  76. furi_hal_gpio_write(pin_up, (coleco->button_code & 8));
  77. }
  78. }
  79. if (events & WorkerEvtUpChange)
  80. {
  81. if (coleco->mode)
  82. {
  83. furi_hal_gpio_write(pin_up, !coleco->button_up);
  84. }
  85. }
  86. if (events & WorkerEvtDownChange)
  87. {
  88. if (coleco->mode)
  89. {
  90. furi_hal_gpio_write(pin_down, !coleco->button_down);
  91. }
  92. }
  93. if (events & WorkerEvtRightChange)
  94. {
  95. if (coleco->mode)
  96. {
  97. furi_hal_gpio_write(pin_right, !coleco->button_right);
  98. }
  99. }
  100. if (events & WorkerEvtLeftChange)
  101. {
  102. if (coleco->mode)
  103. {
  104. furi_hal_gpio_write(pin_left, !coleco->button_left);
  105. }
  106. }
  107. if (events & WorkerEvtCodeChange)
  108. {
  109. if (!coleco->mode)
  110. {
  111. furi_hal_gpio_write(pin_left, !(coleco->button_code & 1));
  112. furi_hal_gpio_write(pin_down, !(coleco->button_code & 2));
  113. furi_hal_gpio_write(pin_right, !(coleco->button_code & 4));
  114. furi_hal_gpio_write(pin_up, !(coleco->button_code & 8));
  115. }
  116. }
  117. }
  118. return 0;
  119. }
  120. static void render_callback(Canvas* const canvas, void* context)
  121. {
  122. Coleco* coleco = acquire_mutex((ValueMutex*)context, 25);
  123. if (coleco == NULL)
  124. {
  125. return;
  126. }
  127. canvas_set_font(canvas, FontPrimary);
  128. canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, "ColecoVision");
  129. canvas_set_font(canvas, FontSecondary);
  130. if (coleco->dpad)
  131. {
  132. canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignBottom, "d-pad");
  133. }
  134. else
  135. {
  136. canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignBottom, "numbers");
  137. }
  138. release_mutex((ValueMutex*)context, coleco);
  139. }
  140. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue)
  141. {
  142. furi_assert(event_queue);
  143. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  144. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  145. }
  146. static Coleco* coleco_alloc()
  147. {
  148. Coleco* coleco = malloc(sizeof(Coleco));
  149. coleco->button_panel = button_panel_alloc();
  150. coleco->mode = true;
  151. coleco->button_up = false;
  152. coleco->button_down = false;
  153. coleco->button_right = false;
  154. coleco->button_left = false;
  155. coleco->button_fire = false;
  156. coleco->button_code = 0x0F;
  157. coleco->dpad = false;
  158. // configure output pins
  159. furi_hal_gpio_init(pin_up, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  160. furi_hal_gpio_init(pin_down, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  161. furi_hal_gpio_init(pin_right, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  162. furi_hal_gpio_init(pin_left, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  163. furi_hal_gpio_init(pin_fire, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  164. furi_hal_gpio_write(pin_up, true);
  165. furi_hal_gpio_write(pin_down, true);
  166. furi_hal_gpio_write(pin_right, true);
  167. furi_hal_gpio_write(pin_left, true);
  168. furi_hal_gpio_write(pin_fire, true);
  169. // configure mode pin
  170. furi_hal_gpio_init(pin_mode, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh);
  171. // set up output worker thread
  172. coleco->thread = furi_thread_alloc();
  173. furi_thread_set_name(coleco->thread, "ColecoOutputWorker");
  174. furi_thread_set_stack_size(coleco->thread, 1024);
  175. furi_thread_set_context(coleco->thread, coleco);
  176. furi_thread_set_callback(coleco->thread, coleco_output_worker);
  177. furi_thread_start(coleco->thread);
  178. return coleco;
  179. }
  180. static void coleco_free(Coleco* coleco)
  181. {
  182. furi_assert(coleco);
  183. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtStop);
  184. furi_thread_join(coleco->thread);
  185. furi_thread_free(coleco->thread);
  186. button_panel_free(coleco->button_panel);
  187. free(coleco);
  188. }
  189. int32_t coleco_app(void* p)
  190. {
  191. UNUSED(p);
  192. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  193. Coleco* coleco = coleco_alloc();
  194. ValueMutex coleco_mutex;
  195. if (!init_mutex(&coleco_mutex, coleco, sizeof(Coleco)))
  196. {
  197. FURI_LOG_E("Coleco", "cannot create mutex\r\n");
  198. coleco_free(coleco);
  199. return 255;
  200. }
  201. // set system callbacks
  202. ViewPort* view_port = view_port_alloc();
  203. view_port_draw_callback_set(view_port, render_callback, &coleco_mutex);
  204. view_port_input_callback_set(view_port, input_callback, event_queue);
  205. // open GUI and register view_port
  206. Gui* gui = furi_record_open("gui");
  207. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  208. PluginEvent event;
  209. for (bool processing = true; processing;)
  210. {
  211. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  212. Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex);
  213. if (event_status == FuriStatusOk)
  214. {
  215. // press events
  216. if (event.type == EventTypeKey)
  217. {
  218. switch (event.input.key)
  219. {
  220. case InputKeyUp:
  221. if (coleco->dpad)
  222. {
  223. if (event.input.type == InputTypePress)
  224. {
  225. coleco->button_up = true;
  226. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtUpChange);
  227. }
  228. else if (event.input.type == InputTypeRelease)
  229. {
  230. coleco->button_up = false;
  231. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtUpChange);
  232. }
  233. }
  234. else // FIXME: hack to allow the 1 to be pressed
  235. {
  236. if (event.input.type == InputTypePress)
  237. {
  238. coleco->button_code = 0x0D;
  239. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtCodeChange);
  240. }
  241. else if (event.input.type == InputTypeRelease)
  242. {
  243. coleco->button_code = 0x0F;
  244. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtCodeChange);
  245. }
  246. }
  247. break;
  248. case InputKeyDown:
  249. if (coleco->dpad)
  250. {
  251. if (event.input.type == InputTypePress)
  252. {
  253. coleco->button_down = true;
  254. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtDownChange);
  255. }
  256. else if (event.input.type == InputTypeRelease)
  257. {
  258. coleco->button_down = false;
  259. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtDownChange);
  260. }
  261. }
  262. break;
  263. case InputKeyRight:
  264. if (coleco->dpad)
  265. {
  266. if (event.input.type == InputTypePress)
  267. {
  268. coleco->button_right = true;
  269. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtRightChange);
  270. }
  271. else if (event.input.type == InputTypeRelease)
  272. {
  273. coleco->button_right = false;
  274. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtRightChange);
  275. }
  276. }
  277. break;
  278. case InputKeyLeft:
  279. if (coleco->dpad)
  280. {
  281. if (event.input.type == InputTypePress)
  282. {
  283. coleco->button_left = true;
  284. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtLeftChange);
  285. }
  286. else if (event.input.type == InputTypeRelease)
  287. {
  288. coleco->button_left = false;
  289. furi_thread_flags_set(furi_thread_get_id(coleco->thread), WorkerEvtLeftChange);
  290. }
  291. }
  292. break;
  293. case InputKeyOk:
  294. if (coleco->dpad)
  295. {
  296. if (event.input.type == InputTypePress)
  297. {
  298. coleco->button_fire = true;
  299. furi_hal_gpio_write(pin_fire, !coleco->button_fire);
  300. }
  301. else if (event.input.type == InputTypeRelease)
  302. {
  303. coleco->button_fire = false;
  304. furi_hal_gpio_write(pin_fire, !coleco->button_fire);
  305. }
  306. }
  307. else
  308. {
  309. coleco->dpad = true;
  310. }
  311. break;
  312. case InputKeyBack:
  313. if (event.input.type == InputTypePress)
  314. {
  315. if (coleco->dpad)
  316. {
  317. coleco->dpad = false;
  318. }
  319. else
  320. {
  321. processing = false;
  322. }
  323. }
  324. break;
  325. default:
  326. break;
  327. }
  328. view_port_update(view_port);
  329. }
  330. }
  331. else
  332. {
  333. FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout");
  334. }
  335. release_mutex(&coleco_mutex, coleco);
  336. }
  337. furi_hal_gpio_remove_int_callback(pin_mode);
  338. view_port_enabled_set(view_port, false);
  339. gui_remove_view_port(gui, view_port);
  340. furi_record_close("gui");
  341. view_port_free(view_port);
  342. furi_message_queue_free(event_queue);
  343. delete_mutex(&coleco_mutex);
  344. coleco_free(coleco);
  345. return 0;
  346. }