Commit MetaInfo

Revisión2897613916f8f360d70faeb899a4fde6d1879a97 (tree)
Tiempo2020-02-28 10:03:58
AutorDavid Ludwig <dludwig@pobo...>
CommiterDavid Ludwig

Log Message

add preliminary support for joystick-hats, and for SDL's Game Controller API

Cambiar Resumen

Diferencia

diff -r b9c6e714470f -r 2897613916f8 main.cpp
--- a/main.cpp Tue Feb 25 16:40:53 2020 -0500
+++ b/main.cpp Thu Feb 27 20:03:58 2020 -0500
@@ -22,8 +22,10 @@
2222 #include <limits>
2323 #include <mutex>
2424 #include <numeric>
25+#include <sstream>
2526 #include <string>
2627 #include <type_traits>
28+#include <vector>
2729
2830 #include "SDL.h"
2931 #include "imgui.h"
@@ -82,6 +84,30 @@
8284 return JoystickTypeName(SDL_JOYSTICK_TYPE_UNKNOWN);
8385 }
8486
87+struct ControllerTypeInfo {
88+ SDL_GameControllerType type;
89+ const char * name;
90+};
91+static const ControllerTypeInfo controller_type_infos[] = {
92+ { SDL_CONTROLLER_TYPE_UNKNOWN, "Unknown" },
93+ { SDL_CONTROLLER_TYPE_XBOX360, "XBox 360" },
94+ { SDL_CONTROLLER_TYPE_XBOXONE, "XBox One" },
95+ { SDL_CONTROLLER_TYPE_PS3, "PS3" },
96+ { SDL_CONTROLLER_TYPE_PS4, "PS4" },
97+ { SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO, "Nintendo Switch Pro" },
98+ { SDL_CONTROLLER_TYPE_VIRTUAL, "Virtual" },
99+};
100+
101+constexpr const char * ControllerTypeName(SDL_GameControllerType type)
102+{
103+ for (int i = 0; i < SDL_arraysize(controller_type_infos); ++i) {
104+ if (type == controller_type_infos[i].type) {
105+ return controller_type_infos[i].name;
106+ }
107+ }
108+ return ControllerTypeName(SDL_CONTROLLER_TYPE_UNKNOWN);
109+}
110+
85111 void ShowJoystickSubWindow(int device_index);
86112
87113 void ShowJoystickMainWindow()
@@ -92,21 +118,23 @@
92118 } main_columns[] = {
93119 { 35.f, "" },
94120 { 60.f, "Actions" },
95- { 95.f, "Device Index" },
96- { 260.f, "Name" },
97- { 95.f, "Player Index" },
98- { 265.f, "Device GUID" },
121+ { 55.f, "Device\nIndex" },
122+ { 250.f, "Name" },
123+ { 60.f, "Player\nIndex" },
124+ { 265.f, "Device\nGUID" },
99125 { 118.f, "Type" },
100- { 90.f, "Instance ID" },
126+ { 70.f, "Instance\nID" },
101127 { 70.f, "Virtual?" },
128+ { 110.f, "Game\nController?"},
102129 };
103- // const float total_width = std::accumulate(main_columns, main_columns + SDL_arraysize(main_columns), 0);
104130 float total_width = 0.f;
105131 for (const auto & col : main_columns) {
106132 total_width += col.width;
107133 }
134+
108135 const float window_margins = 1;
109- const float main_window_height = 480.f;
136+ // const float main_window_height = 480.f;
137+ const float main_window_height = 650.f;
110138
111139 ImGui::SetNextWindowPos(ImVec2(window_margins, 20.f + window_margins), ImGuiCond_Once);
112140 ImGui::SetNextWindowSize(ImVec2(total_width, main_window_height), ImGuiCond_Once);
@@ -244,7 +272,17 @@
244272
245273 const SDL_bool is_joystick_virtual = SDL_JoystickIsVirtual(device_index);
246274 ImGui::Text("%s", (is_joystick_virtual == SDL_TRUE ? "YES" : "NO"));
275+ ImGui::NextColumn();
247276
277+ const SDL_bool is_game_controller = SDL_IsGameController(device_index);
278+ ImGui::Text("%s", (is_game_controller == SDL_TRUE ? "YES" : "NO"));
279+ // if (is_game_controller) {
280+ // ImGui::SameLine();
281+ // ImGui::Dummy(ImVec2(8,8));
282+ // ImGui::SameLine();
283+ // if (ImGui::Button("Show...")) {
284+ // }
285+ // }
248286 ImGui::NextColumn();
249287
250288 if (tree_node_is_open) {
@@ -280,7 +318,8 @@
280318
281319 char window_name[64];
282320 SDL_snprintf(window_name, SDL_arraysize(window_name), "Joystick #%d", device_index);
283- ImGui::BeginChild(window_name, ImVec2(0,300), true, 0);
321+ // ImGui::BeginChild(window_name, ImVec2(0,300), true, 0);
322+ ImGui::BeginChild(window_name, ImVec2(0,550), true, 0);
284323
285324 ImGui::Text("Is Virtual?: %s", (is_joystick_virtual == SDL_TRUE ? "YES" : "NO"));
286325
@@ -303,17 +342,10 @@
303342 { "Edit:", is_joystick_virtual == SDL_TRUE },
304343 };
305344
306- int num_rows = 0;
307- for (int i = 0; i < SDL_arraysize(rowLabels); ++i) {
308- if (rowLabels[i].show) {
309- num_rows++;
310- }
311- }
312-
313345 ImGui::SetColumnWidth(0, 85);
314346 for (int i = 1; i < nbuttons + 1; ++i) {
315347 // ImGui::SetColumnWidth(i, ImGui::GetWindowWidth() / (nbuttons + 1));
316- ImGui::SetColumnWidth(i, 70);
348+ ImGui::SetColumnWidth(i, 40);
317349 }
318350
319351 for (int row = 0; row < SDL_arraysize(rowLabels); ++row) {
@@ -476,11 +508,337 @@
476508 ImGui::PushID("Hats");
477509 const int nhats = SDL_JoystickNumHats(joystick);
478510 ImGui::Text("Hats: (count = %d)", nhats);
511+ if (nhats > 0) {
512+ struct {
513+ const char * name;
514+ bool show;
515+ } rowLabels[] = {
516+ { "Index:", true },
517+ { "State:", true },
518+ { "Raw State:", true },
519+ };
520+
521+ ImGui::Columns(nhats + 1);
522+ ImGui::SetColumnWidth(0, 85);
523+ for (int i = 1; i < nhats + 1; ++i) {
524+ // ImGui::SetColumnWidth(i, ImGui::GetWindowWidth() / (nbuttons + 1));
525+ ImGui::SetColumnWidth(i, 200);
526+ }
527+
528+ for (int row = 0; row < SDL_arraysize(rowLabels); ++row) {
529+ if (!rowLabels[row].show) {
530+ continue;
531+ }
532+ ImGui::PushID(row);
533+ ImGui::Text("%s", rowLabels[row].name);
534+ ImGui::NextColumn();
535+
536+ for (int col = 0; col < nhats; ++col) {
537+ ImGui::PushID(col);
538+ const Uint8 hatState = SDL_JoystickGetHat(joystick, col);
539+ switch (row) {
540+ case 0: {
541+ // display hat-index
542+ ImGui::Indent(4);
543+ ImGui::PushStyleColor(ImGuiCol_Text, (ImVec4)ImColor(127, 255, 127));
544+ ImGui::Text("#%d", col);
545+ ImGui::PopStyleColor(1);
546+ ImGui::Unindent(4);
547+ break;
548+ }
549+ case 1: {
550+ // display hat
551+ const ImGuiWindowFlags child_window_flags = \
552+ ImGuiWindowFlags_NoScrollbar;
553+ const float cell_size = 24.f;
554+ const float grid_margins = 4.f;
555+ const int grid_span = 3;
556+ const float grid_total_size = (grid_span*cell_size) + ((grid_span-1)*grid_margins);
557+ ImGui::BeginChild("Foo", ImVec2(grid_total_size,grid_total_size), false, child_window_flags);
558+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(grid_margins,grid_margins));
559+ const Uint8 hats[] = {
560+ SDL_HAT_LEFTUP,
561+ SDL_HAT_UP,
562+ SDL_HAT_RIGHTUP,
563+ SDL_HAT_LEFT,
564+ SDL_HAT_CENTERED,
565+ SDL_HAT_RIGHT,
566+ SDL_HAT_LEFTDOWN,
567+ SDL_HAT_DOWN,
568+ SDL_HAT_RIGHTDOWN
569+ };
570+ for (int row = 0; row < 3; ++row) {
571+ ImGui::PushID(row);
572+ for (int col = 0; col < 3; ++col) {
573+ ImGui::PushID(col);
574+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.f);
575+ ImGui::PushStyleColor(ImGuiCol_Border, (ImVec4)ImColor(64,64,64));
576+ if (hats[(row*3)+col] == hatState) {
577+ ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor(255,255,0));
578+ } else {
579+ ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor(0,0,0));
580+ }
581+ ImGui::Button("", ImVec2(cell_size,cell_size));
582+ ImGui::PopStyleColor(2);
583+ ImGui::PopStyleVar(1);
584+ if (col+1 < 3) {
585+ ImGui::SameLine();
586+ }
587+ ImGui::PopID();
588+ }
589+ ImGui::PopID();
590+ }
591+ ImGui::PopStyleVar();
592+ ImGui::EndChild();
593+ break;
594+ };
595+ case 2: {
596+ // display raw state
597+ ImGui::PushStyleColor(ImGuiCol_Text, (ImVec4)ImColor(255, 255, 255));
598+ ImGui::Text("0x%x", hatState);
599+ ImGui::PopStyleColor(1);
600+ break;
601+ }
602+ }
603+
604+ ImGui::NextColumn();
605+ ImGui::PopID(); // col
606+ }
607+ ImGui::PopID(); // row
608+ }
609+
610+ ImGui::Columns(1);
611+ }
479612 ImGui::PopID();
480613
481614 ImGui::EndChild();
482615 }
483616
617+static void ShowGameControllersMainWindow()
618+{
619+ struct {
620+ float width;
621+ const char *name;
622+ } main_columns[] = {
623+ { 35.f, "" },
624+ { 60.f, "Actions" }, // OK
625+ { 70.f, "Joystick\nDevice\nIndex" }, // OK
626+ { 250.f, "Name" }, // ok
627+ { 60.f, "Player\nIndex" }, // ok
628+ { 150.f, "Type" }, // ok
629+ { 95.f, "Has Mapping?" },
630+ { 55.f, "Vendor" },
631+ { 60.f, "Product" },
632+ { 65.f, "Product\nVersion" },
633+ { 75.f, "Attached?" },
634+ };
635+ float total_width = 0.f;
636+ for (const auto & col : main_columns) {
637+ total_width += col.width;
638+ }
639+
640+ const float window_margins = 1;
641+ const float main_window_height = 480.f;
642+
643+ ImGui::SetNextWindowPos(ImVec2(window_margins + 20.f, 200.f + window_margins), ImGuiCond_Once);
644+ ImGui::SetNextWindowSize(ImVec2(total_width, main_window_height), ImGuiCond_Once);
645+ ImGui::Begin("Game Controllers", NULL, 0);
646+
647+ // if (ImGui::Button("Add Virtual Game Controller")) {
648+ // }
649+
650+ std::vector<int> controller_indices;
651+ int num_controllers = 0;
652+ for (int i = 0, num_joysticks = SDL_NumJoysticks(); i < num_joysticks; ++i) {
653+ if (SDL_IsGameController(i)) {
654+ num_controllers++;
655+ controller_indices.push_back(i);
656+ }
657+ }
658+ ImGui::SameLine();
659+ ImGui::Dummy(ImVec2(32,0));
660+ ImGui::SameLine();
661+ ImGui::Text("Controllers, Total-Count: %d", num_controllers);
662+
663+ ImGui::Separator();
664+ ImGui::Separator();
665+
666+ ImGui::Columns(SDL_arraysize(main_columns), "ControllerList", true);
667+ for (int i = 0; i < SDL_arraysize(main_columns); ++i) {
668+ ImGui::SetColumnWidth(i, main_columns[i].width);
669+ }
670+ for (int i = 0; i < SDL_arraysize(main_columns); ++i) {
671+ ImGui::Text("%s", main_columns[i].name);
672+ ImGui::NextColumn();
673+ }
674+
675+ ImGui::Separator();
676+
677+ // for (int device_index = 0; device_index < num_joysticks; ++device_index) {
678+ for (int device_index : controller_indices) {
679+ ImGui::PushID(device_index);
680+ SDL_JoystickID device_instance_id = SDL_JoystickGetDeviceInstanceID(device_index);
681+ SDL_GameController * controller = SDL_GameControllerFromInstanceID(device_instance_id);
682+ // SDL_Joystick * joystick = SDL_JoystickFromInstanceID(device_instance_id);
683+
684+ ImGuiTreeNodeFlags tree_node_flags = 0;
685+ // if (joystick) {
686+ // tree_node_flags |= ImGuiTreeNodeFlags_DefaultOpen;
687+ // }
688+ const bool tree_node_is_open = ImGui::TreeNodeEx((void*)(intptr_t)device_index, tree_node_flags, ""); //, "Joystick #%d", device_index);
689+ ImGui::NextColumn();
690+
691+ // Actions
692+ if (controller) {
693+ if (ImGui::Button("Close")) {
694+ SDL_GameControllerClose(controller);
695+ controller = nullptr;
696+ }
697+ } else {
698+ if (ImGui::Button("Open")) {
699+ controller = SDL_GameControllerOpen(device_index);
700+ }
701+ }
702+ ImGui::NextColumn();
703+
704+ // Joystick Device Index
705+ ImGui::Text("%d", device_index);
706+ ImGui::NextColumn();
707+
708+ // Name
709+ ImGui::Text("%s", SDL_GameControllerNameForIndex(device_index));
710+ ImGui::NextColumn();
711+
712+ // Player Index
713+ if (controller) {
714+ ImGui::Text("%d", SDL_GameControllerGetPlayerIndex(controller));
715+ }
716+ ImGui::NextColumn();
717+
718+ // Type
719+ ImGui::Text("%s", ControllerTypeName(SDL_GameControllerTypeForIndex(device_index)));
720+ ImGui::NextColumn();
721+
722+ // Mapping
723+ const char * mapping = SDL_GameControllerMappingForDeviceIndex(device_index);
724+ ImGui::Text("%s", (mapping ? "YES" : "NO"));
725+ if (mapping) {
726+ ImGui::SameLine();
727+ static bool did_copy = false;
728+ if (ImGui::Button("Show...")) {
729+ did_copy = false;
730+ ImGui::OpenPopup("Show Mapping");
731+ }
732+ if (ImGui::IsPopupOpen("Show Mapping")) {
733+ ImGui::SetNextWindowSize(ImVec2(400,480));
734+ }
735+ if (ImGui::BeginPopup("Show Mapping")) {
736+ if (ImGui::Button("Copy to clipboard")) {
737+ if (SDL_SetClipboardText(mapping) == 0) { // SDL_SetClipboardText() will convert NULL to "", here
738+ did_copy = true;
739+ }
740+ }
741+ if (did_copy) {
742+ ImGui::SameLine();
743+ ImGui::Text("copied!");
744+ }
745+ ImGui::Separator();
746+ static bool show_unformatted = false;
747+ ImGui::Checkbox("Show unformatted?", &show_unformatted);
748+ ImGui::Separator();
749+
750+ if (show_unformatted || !mapping) {
751+ // const float wrap_width = 235.f;
752+ const float wrap_width = ImGui::GetWindowWidth() - 16.f;
753+ ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
754+ ImGui::Text("%s", mapping);
755+ ImGui::PopTextWrapPos();
756+ } else {
757+ ImGui::Columns(2);
758+ ImGui::SetColumnWidth(0, 110.f);
759+ ImGui::SetColumnWidth(1, 250.f);
760+
761+ std::istringstream iss(mapping);
762+ std::string item;
763+ for (int count = 0; std::getline(iss, item, ','); ++count) {
764+ std::string name;
765+ std::string value;
766+ const size_t colon_pos = item.find(':');
767+ if (colon_pos != std::string::npos) {
768+ name = item.substr(0,colon_pos);
769+ value = item.substr(colon_pos + 1);
770+ } else {
771+ switch (count) {
772+ case 0:
773+ name = "guid";
774+ break;
775+ case 1:
776+ name = "name";
777+ break;
778+ }
779+ value = item.c_str();
780+ }
781+ ImGui::Text("%s", name.c_str());
782+ ImGui::NextColumn();
783+ ImGui::Text("%s", value.c_str());
784+ ImGui::NextColumn();
785+ }
786+
787+ ImGui::Columns(1);
788+ }
789+
790+ ImGui::EndPopup();
791+ }
792+ }
793+ ImGui::NextColumn();
794+
795+ // Vendor
796+ if (controller) {
797+ ImGui::Text("0x%04x", (int)SDL_GameControllerGetVendor(controller));
798+ }
799+ ImGui::NextColumn();
800+
801+ // Product
802+ if (controller) {
803+ ImGui::Text("0x%04x", (int)SDL_GameControllerGetProduct(controller));
804+ }
805+ ImGui::NextColumn();
806+
807+ // Product Version
808+ if (controller) {
809+ ImGui::Text("0x%04x", (int)SDL_GameControllerGetProductVersion(controller));
810+ }
811+ ImGui::NextColumn();
812+
813+ // Attached?
814+ if (controller) {
815+ ImGui::Text("%s", (SDL_GameControllerGetAttached(controller) == SDL_TRUE ? "YES" : "NO"));
816+ }
817+ ImGui::NextColumn();
818+
819+ if (tree_node_is_open) {
820+ // setup new state
821+ ImGui::PushID(device_index);
822+ ImGui::Columns(1);
823+
824+ // show sub-content
825+ // ShowJoystickSubWindow(device_index);
826+
827+ // restore state
828+ ImGui::TreePop();
829+ ImGui::Columns(SDL_arraysize(main_columns), "JoystickList", true);
830+ for (int i = 0; i < SDL_arraysize(main_columns); ++i) {
831+ ImGui::SetColumnWidth(i, main_columns[i].width);
832+ }
833+ ImGui::PopID();
834+ }
835+
836+ ImGui::PopID();
837+ }
838+
839+ ImGui::End();
840+}
841+
484842 static bool isRunning = true;
485843 static SDL_Renderer * renderer = nullptr;
486844
@@ -547,8 +905,10 @@
547905 // Layout this frame's ImGui
548906 static bool show_window_imgui_demo = false;
549907 static bool show_window_joysticks = true;
908+ static bool show_window_game_controllers = false;
550909 if (ImGui::BeginMainMenuBar()) {
551910 if (ImGui::BeginMenu("View")) {
911+ ImGui::MenuItem("Game Controllers...", NULL, &show_window_game_controllers);
552912 ImGui::MenuItem("ImGui Demo Window...", NULL, &show_window_imgui_demo);
553913 ImGui::MenuItem("Joysticks...", NULL, &show_window_joysticks);
554914 ImGui::EndMenu();
@@ -561,6 +921,9 @@
561921 if (show_window_joysticks) {
562922 ShowJoystickMainWindow();
563923 }
924+ if (show_window_game_controllers) {
925+ ShowGameControllersMainWindow();
926+ }
564927
565928 // Tell ImGui + SDL that we're done drawing, for now
566929 SDL_SetRenderDrawColor(renderer, 114, 144, 154, 255);
@@ -573,7 +936,7 @@
573936 int main()
574937 {
575938 // Initialize SDL and ImGui
576- const int sysWindowWidth = 1280;
939+ const int sysWindowWidth = 1100;
577940 const int sysWindowHeight = 700;
578941 SDL_Init(SDL_INIT_EVERYTHING);
579942 SDL_Window * window = SDL_CreateWindow(
@@ -596,7 +959,7 @@
596959
597960 #if DEV_DAVIDL
598961 // DEBUG: add a virtual joystick
599- if (1) {
962+ if (0) {
600963 const int vjoy_device_index = SDL_JoystickAttachVirtual(SDL_JOYSTICK_TYPE_GAMECONTROLLER, 2, 0, 4, 0);
601964 if (vjoy_device_index >= 0) {
602965 SDL_JoystickOpen(vjoy_device_index);
Show on old repository browser