Skip to content

Commit ba615eb

Browse files
committed
Initial release
1 parent 8a2b1f4 commit ba615eb

File tree

6 files changed

+303
-7
lines changed

6 files changed

+303
-7
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
pyimgui-orig/
1+
pyimgui_orig/
2+
imgui_orig/
3+
.bootstrapped
24
.DS_Store

.gitmodules

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
[submodule "imgui"]
99
path = imgui
1010
url = https://github.com/ocornut/imgui.git
11+
ignore = dirty
12+

Makefile

+39-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11

2-
32
.PHONY: createPatch createPyimguiPatch
43

5-
applyPatch: applyPyimguiPatch
4+
all: dist
5+
6+
dist: bootstrap applyPatch buildPyimgui
7+
cp -r pyimgui/build/python_files/imgui dist/python/
8+
cp pyimgui/build/c_files/__cwrap_pyimgui_core.* dist/modules
9+
10+
cp imgui_software_renderer/src/imgui_sw.* dist/modules/imguihelper/imgui/
11+
cp imgui/*.h dist/modules/imguihelper/imgui/
12+
cp imgui/*.cpp dist/modules/imguihelper/imgui/
13+
cp imguihelper/imguihelper.cpp dist/modules/imguihelper/
14+
15+
16+
bootstrap: .bootstrapped
17+
18+
.bootstrapped:
19+
git submodule update --init
20+
mkdir -p dist/modules/imguihelper/imgui
21+
mkdir -p dist/python
22+
@touch .bootstrapped
23+
24+
buildPyimgui: applyPyimguiPatch
25+
cd pyimgui; python3 build.py
26+
27+
applyPatch: applyPyimguiPatch applyImguiPatch
628

729
applyPyimguiPatch:
830
patch -p0 < patch/pyimgui.patch
931

10-
createPatch: createPyimguiPatch
11-
1232
createPyimguiPatch: pyimgui_orig
1333
-diff -Naur --exclude=".git" --exclude="build" --exclude="imgui-cpp" pyimgui_orig pyimgui > patch/pyimgui.patch
1434

@@ -18,6 +38,19 @@ pyimgui_orig:
1838
git clone https://github.com/swistakm/pyimgui.git pyimgui_orig
1939
cd pyimgui_orig; git reset --hard $(PYIMGUI_COMMIT)
2040

41+
applyImguiPatch:
42+
patch -p0 < patch/imgui.patch
43+
44+
createImguiPatch: imgui_orig
45+
-diff -Naur --exclude=".git" imgui_orig imgui > patch/imgui.patch
46+
47+
IMGUI_COMMIT := $(shell git ls-files -s imgui | awk '{print $$2}')
48+
imgui_orig:
49+
@echo Current imgui commit hash: $(IMGUI_COMMIT)
50+
git clone https://github.com/ocornut/imgui imgui_orig
51+
cd imgui_orig; git reset --hard $(IMGUI_COMMIT)
52+
2153
clean:
22-
rm -rf pyimgui_orig
23-
git submodule foreach --recursive git reset --hard
54+
rm -rf pyimgui_orig imgui_orig dist .bootstrapped
55+
git submodule foreach --recursive git reset --hard
56+
cd pyimgui && git clean -f -d

imguihelper/imguihelper.cpp

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Adapted from https://github.com/nx-python/imgui-switch/blob/master/src/main.cpp
2+
#include <switch.h>
3+
4+
#include "Python.h"
5+
6+
#include "imgui/imgui.h"
7+
#include "imgui/imgui_sw.hpp"
8+
9+
#include <vector>
10+
11+
u32 * framebuf;
12+
int width, height;
13+
std::vector<uint32_t> * pixel_buffer;
14+
bool initialized = false;
15+
16+
PyDoc_STRVAR(module_doc, "Helper functions for pyimgui on switch\n");
17+
18+
19+
PyDoc_STRVAR(imguihelper_initialize_doc, ""); // TODO
20+
21+
static PyObject *
22+
imguihelper_initialize(PyObject *self, PyObject *args)
23+
{
24+
gfxSetMode(GfxMode_LinearDouble);
25+
26+
if (initialized) Py_RETURN_NONE;
27+
28+
29+
framebuf = (u32*) gfxGetFramebuffer((u32*)&width, (u32*)&height);
30+
pixel_buffer = new std::vector<uint32_t>(width * height, 0);
31+
32+
ImGuiIO& io = ImGui::GetIO();
33+
io.MouseDrawCursor = true;
34+
io.DisplaySize = ImVec2((float)width, (float)height);
35+
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
36+
ImGui::GetStyle().ScaleAllSizes(2);
37+
ImGui::GetStyle().MouseCursorScale = 1;
38+
39+
imgui_sw::bind_imgui_painting();
40+
imgui_sw::make_style_fast();
41+
initialized = true;
42+
43+
Py_RETURN_NONE;
44+
}
45+
46+
47+
48+
PyDoc_STRVAR(imguihelper_handleinputs_doc, ""); // TODO
49+
50+
static PyObject *
51+
imguihelper_handleinputs(PyObject *self, PyObject *args)
52+
{
53+
ImGuiIO& io = ImGui::GetIO();
54+
hidScanInput();
55+
u32 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
56+
JoystickPosition pos;
57+
hidJoystickRead(&pos, CONTROLLER_P1_AUTO, JOYSTICK_LEFT);
58+
59+
memset(io.NavInputs, 0, sizeof(io.NavInputs));
60+
61+
#define MAP_BUTTON(NAV, BUTTON) { if (kHeld & BUTTON) io.NavInputs[NAV] = 1.0f; }
62+
MAP_BUTTON(ImGuiNavInput_Activate, KEY_A);
63+
MAP_BUTTON(ImGuiNavInput_Cancel, KEY_B);
64+
MAP_BUTTON(ImGuiNavInput_Menu, KEY_Y);
65+
MAP_BUTTON(ImGuiNavInput_Input, KEY_X);
66+
MAP_BUTTON(ImGuiNavInput_DpadLeft, KEY_DLEFT);
67+
MAP_BUTTON(ImGuiNavInput_DpadRight, KEY_DRIGHT);
68+
MAP_BUTTON(ImGuiNavInput_DpadUp, KEY_DUP);
69+
MAP_BUTTON(ImGuiNavInput_DpadDown, KEY_DDOWN);
70+
MAP_BUTTON(ImGuiNavInput_FocusPrev, KEY_L);
71+
MAP_BUTTON(ImGuiNavInput_FocusNext, KEY_R);
72+
MAP_BUTTON(ImGuiNavInput_TweakSlow, KEY_L);
73+
MAP_BUTTON(ImGuiNavInput_TweakFast, KEY_R);
74+
MAP_BUTTON(ImGuiNavInput_LStickLeft, KEY_LSTICK_LEFT);
75+
MAP_BUTTON(ImGuiNavInput_LStickRight, KEY_LSTICK_RIGHT);
76+
MAP_BUTTON(ImGuiNavInput_LStickUp, KEY_LSTICK_UP);
77+
MAP_BUTTON(ImGuiNavInput_LStickDown, KEY_LSTICK_DOWN);
78+
#undef MAP_BUTTON
79+
80+
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
81+
82+
83+
// io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX);
84+
io.MouseDown[0] = false;
85+
unsigned long touch_count = hidTouchCount();
86+
if (touch_count > 0) {
87+
touchPosition pos;
88+
hidTouchRead(&pos, 0);
89+
io.MousePos = ImVec2(pos.px, pos.py);
90+
io.MouseDown[0] = true;
91+
}
92+
93+
Py_RETURN_NONE;
94+
}
95+
96+
97+
98+
PyDoc_STRVAR(imguihelper_render_doc, ""); // TODO
99+
100+
static PyObject *
101+
imguihelper_render(PyObject *self, PyObject *args)
102+
{
103+
// fill gray (this could be any previous rendering)
104+
std::fill_n(pixel_buffer->data(), width * height, 0x19191919u);
105+
106+
// overlay the GUI
107+
imgui_sw::paint_imgui(pixel_buffer->data(), width, height);
108+
109+
for (int i = 0; i < height; i++) {
110+
for (int j = 0; j < width; j++) {
111+
u32 pos = i * width + j;
112+
framebuf[pos] = pixel_buffer->data()[pos];
113+
}
114+
}
115+
116+
gfxFlushBuffers();
117+
gfxSwapBuffers();
118+
gfxWaitForVsync();
119+
120+
Py_RETURN_NONE;
121+
}
122+
123+
124+
125+
PyDoc_STRVAR(imguihelper_shutdown_doc, ""); // TODO
126+
127+
static PyObject *
128+
imguihelper_shutdown(PyObject *self, PyObject *args)
129+
{
130+
imgui_sw::unbind_imgui_painting();
131+
delete pixel_buffer;
132+
initialized = false;
133+
134+
Py_RETURN_NONE;
135+
}
136+
137+
138+
139+
static PyMethodDef module_methods[] = {
140+
{"initialize", imguihelper_initialize, METH_NOARGS, imguihelper_initialize_doc},
141+
{"handleinputs", imguihelper_handleinputs, METH_NOARGS, imguihelper_handleinputs_doc},
142+
{"render", imguihelper_render, METH_NOARGS, imguihelper_render_doc},
143+
{"shutdown", imguihelper_shutdown, METH_NOARGS, imguihelper_shutdown_doc},
144+
{NULL, NULL}
145+
};
146+
147+
struct PyModuleDef _imguihelper_Module = {
148+
PyModuleDef_HEAD_INIT,
149+
"imguihelper",
150+
module_doc,
151+
-1,
152+
module_methods,
153+
NULL,
154+
NULL,
155+
NULL,
156+
NULL,
157+
};
158+
159+
PyMODINIT_FUNC
160+
PyInit_imguihelper(void)
161+
{
162+
PyObject *m = PyModule_Create(&_imguihelper_Module);
163+
return m;
164+
}

main.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import imgui
2+
import os
3+
import _nx
4+
import runpy
5+
import sys
6+
from imgui.integrations.nx import NXRenderer
7+
8+
sys.argv = [""] # workaround needed for runpy
9+
10+
def colorToFloat(t):
11+
nt = ()
12+
for v in t:
13+
nt += ((1/255) * v, )
14+
return nt
15+
16+
# (r, g, b)
17+
FOLDER_COLOR = colorToFloat((230, 126, 34))
18+
PYFILE_COLOR = colorToFloat((46, 204, 113))
19+
FILE_COLOR = colorToFloat((41, 128, 185))
20+
21+
22+
TILED_DOUBLE = 1
23+
LINEAR_DOUBLE = 0
24+
def run_python_module(path: str):
25+
_nx.gfx_set_mode(TILED_DOUBLE)
26+
runpy.run_path(path, run_name='__main__')
27+
_nx.gfx_set_mode(LINEAR_DOUBLE)
28+
29+
def main():
30+
renderer = NXRenderer()
31+
currentDir = os.getcwd()
32+
33+
while True:
34+
renderer.handleinputs()
35+
36+
imgui.new_frame()
37+
38+
width, height = renderer.io.display_size
39+
imgui.set_next_window_size(width, height)
40+
imgui.set_next_window_position(0, 0)
41+
imgui.begin("",
42+
flags=imgui.WINDOW_NO_TITLE_BAR | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_SAVED_SETTINGS
43+
)
44+
imgui.text("Welcome to PyNX!")
45+
imgui.text("Touch is supported")
46+
imgui.text("Current dir: " + os.getcwd())
47+
48+
if os.getcwd() != "sdmc:/":
49+
imgui.push_style_color(imgui.COLOR_BUTTON, *FOLDER_COLOR)
50+
if imgui.button("../", width=200, height=60):
51+
os.chdir("..")
52+
imgui.pop_style_color(1)
53+
54+
dirs = []
55+
files = []
56+
for e in os.listdir():
57+
if os.path.isdir(e):
58+
dirs.append(e)
59+
else:
60+
files.append(e)
61+
62+
dirs = sorted(dirs)
63+
files = sorted(files)
64+
65+
for e in dirs:
66+
imgui.push_style_color(imgui.COLOR_BUTTON, *FOLDER_COLOR)
67+
if imgui.button(e + "/", width=200, height=60):
68+
os.chdir(e)
69+
imgui.pop_style_color(1)
70+
71+
for e in files:
72+
if e.endswith(".py"):
73+
imgui.push_style_color(imgui.COLOR_BUTTON, *PYFILE_COLOR)
74+
else:
75+
imgui.push_style_color(imgui.COLOR_BUTTON, *FILE_COLOR)
76+
77+
if imgui.button(e, width=200, height=60) and e.endswith(".py"):
78+
run_python_module(e)
79+
80+
imgui.pop_style_color(1)
81+
82+
83+
imgui.end()
84+
85+
86+
imgui.render()
87+
renderer.render()
88+
89+
renderer.shutdown()
90+
91+
92+
if __name__ == "__main__":
93+
main()

setup.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imguihelper -I$(srcdir)/Modules/imguihelper imguihelper/imguihelper.cpp imguihelper/imgui/imgui_sw.cpp imguihelper/imgui/imgui.cpp imguihelper/imgui/imgui_demo.cpp imguihelper/imgui/imgui_draw.cpp
2+
__cwrap_pyimgui_core -I$(srcdir)/Modules -I$(srcdir)/Modules/imguihelper/imgui __cwrap_pyimgui_core.cpp

0 commit comments

Comments
 (0)