diff --git a/client/devkit_client/gui2/SDL2_image.dll b/client/devkit_client/gui2/SDL2_image.dll new file mode 100644 index 0000000000000000000000000000000000000000..d90de2a3014fe703b7bc954a67fc9cc81c972bf6 Binary files /dev/null and b/client/devkit_client/gui2/SDL2_image.dll differ diff --git a/client/devkit_client/gui2/gui2.py b/client/devkit_client/gui2/gui2.py index 13343ebd20c76220018c4e180358079bce2bd927..860e8e028cfeb5ca830ca588c96999a934ad6803 100644 --- a/client/devkit_client/gui2/gui2.py +++ b/client/devkit_client/gui2/gui2.py @@ -35,6 +35,8 @@ import signalslot logging.getLogger('OpenGL.plugins').setLevel(logging.ERROR) import OpenGL.GL as gl import sdl2 +# requires PySDL2-dll +import sdl2.sdlimage import imgui import imgui.integrations.sdl2 @@ -53,6 +55,8 @@ GUEST_LAN_PATTERN = 'DISABLE' TOGGLE_DEV_MODE = 'If this is not a network issue, please toggle developer mode off then back on for the device and try again.' +ICON_FILENAME = 'logo-steamdeck-256.tga' + logger = logging.getLogger(__name__) @@ -3079,6 +3083,75 @@ class ImGui_SDL2_Viewport: logger.warning('SDL_CreateWindow failed: {}'.format(sdl2.SDL_GetError())) return False + icon_file = os.path.join(devkit_client.ROOT_DIR, ICON_FILENAME).encode() + assert os.path.exists(icon_file) + + # https://github.com/libsdl-org/SDL/blob/SDL2/src/video/x11/SDL_x11window.c#L750 + # SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888); + # https://github.com/libsdl-org/SDL/blob/SDL2/src/video/windows/SDL_windowswindow.c#L639 + # ok, not documented otherwise, but SDL_SetWindowIcon expects ARGB8888 + + icon_image = sdl2.sdlimage.IMG_Load(icon_file) + assert icon_image.contents.format.contents.format == sdl2.SDL_PIXELFORMAT_ARGB8888 + # this works, but requires PySDL2-dll and SDL_Image, and I didn't want it + # NARRATOR: he later learned that was a mistake +# sdl2.SDL_SetWindowIcon(self.sdl_window, icon_image) +# sdl2.SDL_FreeSurface(icon_image) + + # Let's direct load instead! + # Uncompressed TGAs are trivially easy, we don't need sdl2.sdlimage.. + tga = open(icon_file,'rb').read() + assert tga[0] == 0 # no image ID + assert tga[1] == 0 # no colormap + assert tga[2] == 2 # uncompressed true-color image + w = int.from_bytes(tga[12:12+2], byteorder='little', signed=False) + h = int.from_bytes(tga[14:14+2], byteorder='little', signed=False) + depth = tga[16] + assert depth == 32 # RGBA + + # It's a little odd to be giving the line stride in those APIs (w*4), but whatever.. + # BUG (??): does not load any pixels .. you just get a blank icon + # tried variations with SDL_CreateRGBSurfaceFrom and other pixel formats .. none of them worked + tga_surface = sdl2.SDL_CreateRGBSurfaceWithFormatFrom(tga[18:18+w*h*4], w, h, depth, w*4, sdl2.SDL_PIXELFORMAT_ARGB8888) + assert tga_surface is not None + assert tga_surface.contents.format.contents.format == sdl2.SDL_PIXELFORMAT_ARGB8888 + + # Try to force a solid fill? + # BUG (??): that doesn't work either, whatever happened with the surface setup, we can't write to it + pixies = ctypes.cast(tga_surface.contents.pixels, ctypes.POINTER(ctypes.c_uint32)) + i = 0 + while i < w*h: + # ARGB, alpha is the high byte, solid green + pixies[i] = 0xff00ff00 # BGRA32 + i += 1 + + # ^ go ahead, test the above: + #sdl2.SDL_SetWindowIcon(self.sdl_window, tga_surface) + #sdl2.SDL_FreeSurface(tga_surface) + + # Let's create a surface from scratch and see if that works better + # order is red, green, blue, alpha masks. swizzle away + tga_surface = sdl2.SDL_CreateRGBSurface(0, w, h, depth, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000) + assert tga_surface.contents.format.contents.format == sdl2.SDL_PIXELFORMAT_ARGB8888 + + # solid fill + pixies = ctypes.cast(tga_surface.contents.pixels, ctypes.POINTER(ctypes.c_uint32)) + i = 0 + while i < w*h: + # ARGB .. alpha is the high byte, solid green + pixies[i] = 0xff00ff00 + i += 1 + + # smash the image pixels in as is .. victory! + pixies = ctypes.cast(tga_surface.contents.pixels, ctypes.POINTER(ctypes.c_byte)) + i = 0 + while i < w*h*4: + pixies[i] = tga[18+i] + i += 1 + + sdl2.SDL_SetWindowIcon(self.sdl_window, tga_surface) + sdl2.SDL_FreeSurface(tga_surface) + self.gl_context = sdl2.SDL_GL_CreateContext(self.sdl_window) if self.gl_context is None: logger.warning('SDL_GL_CreateContext failed: {}'.format(sdl2.SDL_GetError())) diff --git a/client/logo-steamdeck-256.tga b/client/logo-steamdeck-256.tga new file mode 100644 index 0000000000000000000000000000000000000000..6a7fcfa0c7e21ee84a71c2a90645776da6a0c47d Binary files /dev/null and b/client/logo-steamdeck-256.tga differ diff --git a/requirements.txt b/requirements.txt index 1ba50b796f1226eafe109f264dd2f6106d1d22b9..6d18e28e1e213282a01974d35af303d001c50413 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -PySDL2 +PySDL2-dll PyOpenGL signalslot >= 0.2.0 ifaddr diff --git a/setup/package-linux.py b/setup/package-linux.py index 76db817992df8c2f6b7dc1004c0dc5de253d60c2..714e2648003edba88e002a2e5705001560560b7f 100755 --- a/setup/package-linux.py +++ b/setup/package-linux.py @@ -17,6 +17,10 @@ SETUP_DIR = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) CLIENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../client')) +# cursed, but delicious +sys.path.append(CLIENT_DIR) +from devkit_client.gui2.gui2 import ICON_FILENAME +sys.path.pop() # don't let python buffering get in the way or readable output # https://stackoverflow.com/questions/107705/disable-output-buffering @@ -80,6 +84,7 @@ if __name__ == '__main__': os.path.join(zipappdirname, 'site-packages/devkit-utils') ) shutil.copy(os.path.join(ROOT_DIR, 'ChangeLog'), zipappdirname) + shutil.copy(os.path.join(ROOT_DIR, 'client', ICON_FILENAME), os.path.join(zipappdirname, 'site-packages')) output_path = os.path.abspath(os.path.join(conf.output_directory, output_name)) zipapp.create_archive( zipappdirname, diff --git a/setup/package-windows.py b/setup/package-windows.py index 348527f27fa4dffacdc2d2383c5e452cb1d730c1..d11c52ed653d41032eadb5fcf17a102c83cca0f9 100644 --- a/setup/package-windows.py +++ b/setup/package-windows.py @@ -19,6 +19,7 @@ DIST_DIR = os.path.join(ROOT_DIR, 'dist') # cursed, but delicious sys.path.append(CLIENT_DIR) from devkit_client import locate_cygwin_tools +from devkit_client.gui2.gui2 import ICON_FILENAME sys.path.pop() # don't let python buffering get in the way or readable output @@ -93,6 +94,7 @@ if __name__ == '__main__': shutil.copytree(os.path.join(ROOT_DIR, 'third-party-licenses'), os.path.join(DIST_DIR, 'third-party-licenses'), dirs_exist_ok=True) shutil.copy(os.path.join(ROOT_DIR, 'ChangeLog'), DIST_DIR) + shutil.copy(os.path.join(CLIENT_DIR, ICON_FILENAME), DIST_DIR) zippath = os.path.join(ROOT_DIR, 'devkit-gui-win64.zip') print(f'Creating archive: {zippath}')