From afe4d5beafc44ad1c0c2146cc43c5b362ae9e160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 24 Jul 2026 12:02:16 +0200 Subject: [PATCH] Buildfixes, dependency shuffling to make things more logical --- CMakeLists.txt | 193 ++++++++++--------- Core/HLE/sceUsbCam.cpp | 14 +- Core/HW/Camera.cpp | 314 +----------------------------- Core/HW/Camera.h | 64 ------- Qt/QtMain.cpp | 38 +++- Qt/mainwindow.cpp | 15 +- SDL/MacCameraHelper.mm | 1 + SDL/SDLCocoaMetalLayer.mm | 11 +- SDL/SDLMain.cpp | 388 +++++++++++++++++++++++++++++++++++++- UI/RemoteISOScreen.cpp | 2 +- UWP/PPSSPP_UWPMain.cpp | 5 + headless/Headless.cpp | 1 + ios/IAPManager.mm | 4 + libretro/CMakeLists.txt | 5 +- libretro/libretro.cpp | 8 +- unittest/UnitTest.cpp | 1 + 16 files changed, 558 insertions(+), 506 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90006fe7e2..82f33b6abe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -561,66 +561,6 @@ endmacro() add_subdirectory(ext) -set(CommonJIT - Core/MIPS/JitCommon/JitCommon.cpp - Core/MIPS/JitCommon/JitCommon.h - Core/MIPS/JitCommon/JitBlockCache.cpp - Core/MIPS/JitCommon/JitBlockCache.h - Core/MIPS/JitCommon/JitState.cpp - Core/MIPS/JitCommon/JitState.h -) - -set(CommonX86 - Common/ABI.cpp - Common/ABI.h - Common/CPUDetect.cpp - Common/CPUDetect.h - Common/Thunk.cpp - Common/Thunk.h - Common/x64Analyzer.cpp - Common/x64Analyzer.h - Common/x64Emitter.cpp - Common/x64Emitter.h -) -source_group(x86 FILES ${CommonX86}) - -set(CommonARM - Common/ArmCPUDetect.cpp - Common/ArmEmitter.h - Common/ArmEmitter.cpp -) -source_group(ARM FILES ${CommonARM}) - -set(CommonARM64 - Common/Arm64Emitter.h - Common/Arm64Emitter.cpp - Common/ArmEmitter.h - Common/ArmEmitter.cpp - Core/Util/DisArm64.h - Core/Util/DisArm64.cpp -) -source_group(ARM64 FILES ${CommonARM64}) - -set(CommonRISCV64 - ${CommonJIT} - Common/RiscVCPUDetect.cpp - Common/RiscVEmitter.cpp - Common/RiscVEmitter.h - Core/MIPS/fake/FakeJit.cpp - Core/MIPS/fake/FakeJit.h -) -source_group(RISCV64 FILES ${CommonRISCV64}) - -set(CommonLOONGARCH64 - ${CommonJIT} - Common/LoongArchCPUDetect.cpp - Common/LoongArch64Emitter.cpp - Common/LoongArch64Emitter.h - Core/MIPS/fake/FakeJit.cpp - Core/MIPS/fake/FakeJit.h -) -source_group(LOONGARCH64 FILES ${CommonLOONGARCH64}) - if(WIN32) set(CommonD3D Common/GPU/D3D11/thin3d_d3d11.cpp @@ -651,11 +591,29 @@ include_directories(ext/OpenXR-SDK/include) include_directories(ext) add_library(Common STATIC - ${CommonX86} - ${CommonARM} - ${CommonARM64} - ${CommonRISCV64} - ${CommonLOONGARCH64} + Common/ABI.cpp + Common/ABI.h + Common/CPUDetect.cpp + Common/CPUDetect.h + Common/Thunk.cpp + Common/Thunk.h + Common/x64Analyzer.cpp + Common/x64Analyzer.h + Common/x64Emitter.cpp + Common/x64Emitter.h + Common/ArmCPUDetect.cpp + Common/ArmEmitter.h + Common/ArmEmitter.cpp + Common/Arm64Emitter.h + Common/Arm64Emitter.cpp + Common/ArmEmitter.h + Common/ArmEmitter.cpp + Common/RiscVCPUDetect.cpp + Common/RiscVEmitter.cpp + Common/RiscVEmitter.h + Common/LoongArchCPUDetect.cpp + Common/LoongArch64Emitter.cpp + Common/LoongArch64Emitter.h ${CommonD3D} ${CommonVR} Common/Serialize/Serializer.cpp @@ -857,8 +815,6 @@ add_library(Common STATIC Common/Render/Text/draw_text.h Common/Render/Text/draw_text_android.cpp Common/Render/Text/draw_text_android.h - Common/Render/Text/draw_text_sdl.cpp - Common/Render/Text/draw_text_sdl.h Common/Render/Text/draw_text_win.cpp Common/Render/Text/draw_text_win.h Common/Render/Text/draw_text_uwp.cpp @@ -1455,9 +1411,12 @@ elseif(IOS AND NOT LIBRETRO) # TODO: Enable arc globally? set_source_files_properties(ios/AppDelegate.mm + ios/SceneDelegate.mm + ios/ViewControllerCommon.mm ios/ViewController.mm ios/ViewControllerMetal.mm ios/iOSCoreAudio.mm + ios/IAPManager.mm ios/PPSSPPUIApplication.mm ios/iCade/iCadeReaderView.m ios/main.mm @@ -1561,9 +1520,10 @@ else() UI/PSPNSApplicationDelegate.mm UI/PSPNSApplicationDelegate.h) - set_source_files_properties(Core/Util/DarwinFileSystemServices.mm + set_source_files_properties( + Core/Util/DarwinFileSystemServices.mm UI/PSPNSApplicationDelegate.mm - SDL/CocoaMetalLayer.mm + SDL/SDLCocoaMetalLayer.mm SDL/CocoaBarItems.mm SDL/PPSSPPAboutViewController.m SDL/MacCameraHelper.mm @@ -1572,11 +1532,17 @@ else() PROPERTIES COMPILE_FLAGS -fobjc-arc) list(APPEND targetExtraLibs ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} "-framework AVFoundation" "-framework CoreMedia" "-framework CoreVideo") list(APPEND targetExtraLibs ${SDL_LIB_TARGET}) - elseif(USING_EGL) - list(APPEND targetExtraLibs pthread ${SDL_LIB_TARGET}) else() + list(APPEND targetExtra + Common/Render/Text/draw_text_sdl.cpp + Common/Render/Text/draw_text_sdl.h + ) list(APPEND targetExtraLibs ${SDL_LIB_TARGET}) endif() + + if(USING_EGL AND NOT APPLE) + list(APPEND targetExtraLibs pthread) + endif() endif() if(WIN32) @@ -1722,11 +1688,6 @@ if (IOS AND NOT LIBRETRO) list(APPEND targetExtra ${NativeAppSource}) endif() -if(LIBRETRO) - target_include_directories(core PRIVATE libretro/libretro-common/include) - target_include_directories(${TargetBin} PRIVATE libretro/libretro-common/include) -endif() - if(LINUX AND NOT ANDROID) set(RT_LIB rt) endif() @@ -2522,6 +2483,16 @@ add_library(core STATIC Core/MIPS/MIPSAsm.h Core/MIPS/MIPSTracer.cpp Core/MIPS/MIPSTracer.h + Core/MIPS/JitCommon/JitCommon.cpp + Core/MIPS/JitCommon/JitCommon.h + Core/MIPS/JitCommon/JitBlockCache.cpp + Core/MIPS/JitCommon/JitBlockCache.h + Core/MIPS/JitCommon/JitState.cpp + Core/MIPS/JitCommon/JitState.h + Core/Util/DisArm64.h + Core/Util/DisArm64.cpp + Core/MIPS/fake/FakeJit.cpp + Core/MIPS/fake/FakeJit.h Core/MemFault.cpp Core/MemFault.h Core/MemMap.cpp @@ -2897,8 +2868,6 @@ set(WindowsFiles Windows/stdafx.h ) -list(APPEND LinkCommon ${CMAKE_THREAD_LIBS_INIT}) - if(WIN32) #setup_target_project(${TargetBin} Windows) list(APPEND NativeAppSource ${WindowsFiles}) @@ -2954,6 +2923,19 @@ if(HEADLESS) headless/SDLHeadlessHost.cpp headless/SDLHeadlessHost.h ) + if(APPLE) + list(APPEND HeadlessSource + Common/Render/Text/draw_text_cocoa.mm + Common/Render/Text/draw_text_cocoa.h + ) + set_source_files_properties(Common/Render/Text/draw_text_cocoa.mm + PROPERTIES COMPILE_FLAGS -fobjc-arc) + elseif(SDL_TTF_LIB_TARGET AND NOT WIN32) + list(APPEND HeadlessSource + Common/Render/Text/draw_text_sdl.cpp + Common/Render/Text/draw_text_sdl.h + ) + endif() if(WIN32) list(APPEND HeadlessSource headless/WindowsHeadlessHost.cpp @@ -2971,7 +2953,16 @@ if(HEADLESS) ) endif() add_executable(PPSSPPHeadless ${HeadlessSource}) - target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${LinkCommon} Common core) + target_link_libraries(PPSSPPHeadless ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} core) + if(SDL_LIB_TARGET) + target_link_libraries(PPSSPPHeadless ${SDL_LIB_TARGET}) + endif() + if(SDL_TTF_LIB_TARGET) + target_link_libraries(PPSSPPHeadless ${SDL_TTF_LIB_TARGET}) + endif() + if(FONTCONFIG_LIB_TARGET) + target_link_libraries(PPSSPPHeadless ${FONTCONFIG_LIB_TARGET}) + endif() if(WIN32) target_link_libraries(PPSSPPHeadless d2d1 dwrite d3d11) endif() @@ -2996,13 +2987,35 @@ if(UNITTEST) Core/MIPS/ARM/ArmRegCache.cpp Core/MIPS/ARM/ArmRegCacheFPU.cpp ) + if(APPLE) + target_sources(PPSSPPUnitTest PRIVATE + Common/Render/Text/draw_text_cocoa.mm + Common/Render/Text/draw_text_cocoa.h + ) + set_source_files_properties(Common/Render/Text/draw_text_cocoa.mm + PROPERTIES COMPILE_FLAGS -fobjc-arc) + elseif(SDL_TTF_LIB_TARGET AND NOT WIN32) + target_sources(PPSSPPUnitTest PRIVATE + Common/Render/Text/draw_text_sdl.cpp + Common/Render/Text/draw_text_sdl.h + ) + endif() if(WIN32) target_sources(PPSSPPUnitTest PRIVATE Windows/CaptureDevice.cpp Windows/CaptureDevice.h ) endif() - target_link_libraries(PPSSPPUnitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${LinkCommon} Common core) + target_link_libraries(PPSSPPUnitTest ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} Common core) + if(SDL_LIB_TARGET) + target_link_libraries(PPSSPPUnitTest ${SDL_LIB_TARGET}) + endif() + if(SDL_TTF_LIB_TARGET) + target_link_libraries(PPSSPPUnitTest ${SDL_TTF_LIB_TARGET}) + endif() + if(FONTCONFIG_LIB_TARGET) + target_link_libraries(PPSSPPUnitTest ${FONTCONFIG_LIB_TARGET}) + endif() if(WIN32) target_link_libraries(PPSSPPUnitTest d2d1 dwrite d3d11) endif() @@ -3037,7 +3050,7 @@ if(ATLAS_TOOL) ext/native/tools/atlastool.cpp ) add_executable(AtlasTool ${AtlasToolSource}) - target_link_libraries(AtlasTool ${LinkCommon} freetype) + target_link_libraries(AtlasTool ${CMAKE_THREAD_LIBS_INIT} freetype) setup_target_project(AtlasTool atlastool) endif() @@ -3116,18 +3129,20 @@ if(TargetBin) target_link_options(${TargetBin} PRIVATE "-Wl,-z,max-page-size=16384") endif() if(TARGET ppsspp_ui) - target_link_libraries(${TargetBin} ${LinkCommon} ${targetExtraLibs} ppsspp_ui) + target_link_libraries(${TargetBin} ${CMAKE_THREAD_LIBS_INIT} ${targetExtraLibs} ppsspp_ui) else() - target_link_libraries(${TargetBin} ${LinkCommon} ${targetExtraLibs} core) + target_link_libraries(${TargetBin} ${CMAKE_THREAD_LIBS_INIT} ${targetExtraLibs} core) endif() endif() -if(ANDROID) - target_link_libraries(${TargetBin} log EGL OpenSLES) -elseif(WIN32) - target_link_libraries(${TargetBin} ws2_32 winmm) -elseif(${CMAKE_SYSTEM_NAME} MATCHES "^(DragonFly|FreeBSD|NetBSD)$") - target_link_libraries(${TargetBin} execinfo) +if(TargetBin) + if(ANDROID) + target_link_libraries(${TargetBin} log EGL OpenSLES) + elseif(WIN32) + target_link_libraries(${TargetBin} ws2_32 winmm) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "^(DragonFly|FreeBSD|NetBSD)$") + target_link_libraries(${TargetBin} execinfo) + endif() endif() # installs diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index c25027b455..d7738250c1 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -332,14 +332,9 @@ std::vector Camera::getDeviceList() { if (winCamera) { return winCamera->getDeviceList(); } - #elif PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) + #else return System_GetCameraDeviceList(); - #elif defined(USING_QT_UI) // Qt:macOS / Qt:Linux - return System_GetCameraDeviceList(); - #elif PPSSPP_PLATFORM(LINUX) // SDL:Linux - return __v4l_getDeviceList(); #endif - return std::vector(); } int Camera::startCapture() { @@ -361,9 +356,6 @@ int Camera::startCapture() { char command[40] = {0}; snprintf(command, sizeof(command), "startVideo_%dx%d", width, height); System_CameraCommand(command); - #elif PPSSPP_PLATFORM(LINUX) - __v4l_startCapture(width, height); - #else ERROR_LOG(Log::HLE, "%s not implemented", __FUNCTION__); #endif return 0; @@ -375,10 +367,8 @@ int Camera::stopCapture() { if (winCamera) { winCamera->sendMessage({ CAPTUREDEVICE_COMMAND::STOP, nullptr }); } -#elif PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI) + #elif PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI) System_CameraCommand("stopVideo"); - #elif PPSSPP_PLATFORM(LINUX) - __v4l_stopCapture(); #else ERROR_LOG(Log::HLE, "%s not implemented", __FUNCTION__); #endif diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp index 2ade8d128d..f91554392b 100644 --- a/Core/HW/Camera.cpp +++ b/Core/HW/Camera.cpp @@ -18,45 +18,7 @@ #include "Camera.h" #include "Core/Config.h" -#ifdef USE_FFMPEG -void convert_frame(int inw, int inh, unsigned char *inData, AVPixelFormat inFormat, - int outw, int outh, unsigned char **outData, int *outLen) { - struct SwsContext *sws_context = sws_getContext( - inw, inh, inFormat, - outw, outh, AV_PIX_FMT_RGB24, - SWS_BICUBIC, NULL, NULL, NULL); - - // resize - uint8_t *src[4] = {0}; - uint8_t *dst[4] = {0}; - int srcStride[4], dstStride[4]; - - unsigned char *rgbData = (unsigned char*)malloc(outw * outh * 4); - - av_image_fill_linesizes(srcStride, inFormat, inw); - av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, outw); - - av_image_fill_pointers(src, inFormat, inh, inData, srcStride); - av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, outh, rgbData, dstStride); - - sws_scale(sws_context, - src, srcStride, 0, inh, - dst, dstStride); - - // compress jpeg - *outLen = outw * outh * 2; - *outData = (unsigned char*)malloc(*outLen); - - jpge::params params; - params.m_quality = 60; - params.m_subsampling = jpge::H2V2; - params.m_two_pass_flag = false; - jpge::compress_image_to_jpeg_file_in_memory( - *outData, *outLen, outw, outh, 3, rgbData, params); - free(rgbData); -} -#endif //USE_FFMPEG - +#include "ext/jpge/jpge.h" void __cameraDummyImage(int width, int height, unsigned char** outData, int* outLen) { #ifdef USE_FFMPEG @@ -65,6 +27,7 @@ void __cameraDummyImage(int width, int height, unsigned char** outData, int* out *outData = nullptr; return; } + // Generate a solid RED surface for camera detection (e.g., Invizimals) // RGB: (255, 0, 0) = Red for (int y = 0; y < height; y++) { @@ -87,276 +50,3 @@ void __cameraDummyImage(int width, int height, unsigned char** outData, int* out free(rgbData); #endif //USE_FFMPEG } - - -#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) - -std::vector __v4l_getDeviceList() { - std::vector deviceList; -#ifdef USE_FFMPEG - for (int i = 0; i < 64; i++) { - char path[256]; - snprintf(path, sizeof(path), "/dev/video%d", i); - if (access(path, F_OK) < 0) { - break; - } - int fd = -1; - if((fd = open(path, O_RDONLY)) < 0) { - ERROR_LOG(Log::HLE, "Cannot open '%s'; errno=%d(%s)", path, errno, strerror(errno)); - continue; - } - struct v4l2_capability video_cap; - if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) < 0) { - ERROR_LOG(Log::HLE, "VIDIOC_QUERYCAP"); - goto cont; - } else { - char device[256]; - snprintf(device, sizeof(device), "%d:%s", i, video_cap.card); - deviceList.push_back(device); - } -cont: - close(fd); - fd = -1; - } -#endif //USE_FFMPEG - return deviceList; -} - -void *v4l_loop(void *data) { -#ifdef USE_FFMPEG - SetCurrentThreadName("v4l_loop"); - while (v4l_fd >= 0) { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - - if (ioctl(v4l_fd, VIDIOC_DQBUF, &buf) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_DQBUF; errno=%d(%s)", errno, strerror(errno)); - switch (errno) { - case EAGAIN: - continue; - default: - return nullptr; - } - } - - unsigned char *jpegData = nullptr; - int jpegLen = 0; - - if (v4l_format == V4L2_PIX_FMT_YUYV) { - convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)v4l_buffers[buf.index].start, AV_PIX_FMT_YUYV422, - v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); - } else if (v4l_format == V4L2_PIX_FMT_JPEG - || v4l_format == V4L2_PIX_FMT_MJPEG) { - // decompress jpeg - int width, height, req_comps; - unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory( - (unsigned char*)v4l_buffers[buf.index].start, buf.bytesused, &width, &height, &req_comps, 3); - - convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)rgbData, AV_PIX_FMT_RGB24, - v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); - free(rgbData); - } - - if (jpegData) { - Camera::pushCameraImage(jpegLen, jpegData); - free(jpegData); - jpegData = nullptr; - } - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_QBUF"); - return nullptr; - } - } -#endif //USE_FFMPEG - return nullptr; -} - -int __v4l_startCapture(int ideal_width, int ideal_height) { -#ifdef USE_FFMPEG - if (v4l_fd >= 0) { - __v4l_stopCapture(); - } - v4l_ideal_width = ideal_width; - v4l_ideal_height = ideal_height; - - int dev_index = 0; - char dev_name[64]; - sscanf(g_Config.sCameraDevice.c_str(), "%d:", &dev_index); - snprintf(dev_name, sizeof(dev_name), "/dev/video%d", dev_index); - - if ((v4l_fd = open(dev_name, O_RDWR)) == -1) { - ERROR_LOG(Log::HLE, "Cannot open '%s'; errno=%d(%s)", dev_name, errno, strerror(errno)); - return -1; - } - - struct v4l2_capability cap; - memset(&cap, 0, sizeof(cap)); - if (ioctl(v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_QUERYCAP"); - return -1; - } - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - ERROR_LOG(Log::HLE, "V4L2_CAP_VIDEO_CAPTURE"); - return -1; - } - if (!(cap.capabilities & V4L2_CAP_STREAMING)) { - ERROR_LOG(Log::HLE, "V4L2_CAP_STREAMING"); - return -1; - } - - struct v4l2_format fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.pixelformat = 0; - - // select a pixel format - struct v4l2_fmtdesc desc; - memset(&desc, 0, sizeof(desc)); - desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - while (ioctl(v4l_fd, VIDIOC_ENUM_FMT, &desc) == 0) { - desc.index++; - INFO_LOG(Log::HLE, "V4L2: pixel format supported: %s", desc.description); - if (fmt.fmt.pix.pixelformat != 0) { - continue; - } else if (desc.pixelformat == V4L2_PIX_FMT_YUYV - || desc.pixelformat == V4L2_PIX_FMT_JPEG - || desc.pixelformat == V4L2_PIX_FMT_MJPEG) { - INFO_LOG(Log::HLE, "V4L2: %s selected", desc.description); - fmt.fmt.pix.pixelformat = desc.pixelformat; - v4l_format = desc.pixelformat; - } - } - if (fmt.fmt.pix.pixelformat == 0) { - ERROR_LOG(Log::HLE, "V4L2: No supported format found"); - return -1; - } - - // select a frame size - fmt.fmt.pix.width = 0; - fmt.fmt.pix.height = 0; - struct v4l2_frmsizeenum frmsize; - memset(&frmsize, 0, sizeof(frmsize)); - frmsize.pixel_format = fmt.fmt.pix.pixelformat; - while (ioctl(v4l_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { - frmsize.index++; - if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { - INFO_LOG(Log::HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height); - bool matchesIdeal = frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height; - bool zeroPix = fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0; - bool pixLarger = frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height; - if (matchesIdeal && (zeroPix || pixLarger)) { - fmt.fmt.pix.width = frmsize.discrete.width; - fmt.fmt.pix.height = frmsize.discrete.height; - } - } - } - - if (fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0) { - fmt.fmt.pix.width = ideal_width; - fmt.fmt.pix.height = ideal_height; - } - INFO_LOG(Log::HLE, "V4L2: asking for %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height); - if (ioctl(v4l_fd, VIDIOC_S_FMT, &fmt) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_S_FMT"); - return -1; - } - v4l_hw_width = fmt.fmt.pix.width; - v4l_hw_height = fmt.fmt.pix.height; - INFO_LOG(Log::HLE, "V4L2: will receive %dx%d", v4l_hw_width, v4l_hw_height); - v4l_height_fixed_aspect = v4l_hw_width * ideal_height / ideal_width; - INFO_LOG(Log::HLE, "V4L2: will use %dx%d", v4l_hw_width, v4l_height_fixed_aspect); - - struct v4l2_requestbuffers req; - memset(&req, 0, sizeof(req)); - req.count = 1; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; - if (ioctl(v4l_fd, VIDIOC_REQBUFS, &req) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_REQBUFS"); - return -1; - } - v4l_buffer_count = req.count; - INFO_LOG(Log::HLE, "V4L2: buffer count: %d", v4l_buffer_count); - v4l_buffers = (v4l_buf_t*) calloc(v4l_buffer_count, sizeof(v4l_buf_t)); - - for (int buf_id = 0; buf_id < v4l_buffer_count; buf_id++) { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = buf_id; - if (ioctl(v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_QUERYBUF"); - return -1; - } - - v4l_buffers[buf_id].length = buf.length; - v4l_buffers[buf_id].start = mmap(NULL, - buf.length, - PROT_READ | PROT_WRITE, - MAP_SHARED, - v4l_fd, buf.m.offset); - if (v4l_buffers[buf_id].start == MAP_FAILED) { - ERROR_LOG(Log::HLE, "MAP_FAILED"); - return -1; - } - - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = buf_id; - if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_QBUF"); - return -1; - } - } - - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(v4l_fd, VIDIOC_STREAMON, &type) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_STREAMON"); - return -1; - } - - pthread_create(&v4l_thread, NULL, v4l_loop, NULL); -#endif //USE_FFMPEG - return 0; -} - -int __v4l_stopCapture() { - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (v4l_fd < 0) { - goto exit; - } - - if (ioctl(v4l_fd, VIDIOC_STREAMOFF, &type) == -1) { - ERROR_LOG(Log::HLE, "VIDIOC_STREAMOFF"); - goto exit; - } - - for (int buf_id = 0; buf_id < v4l_buffer_count; buf_id++) { - if (munmap(v4l_buffers[buf_id].start, v4l_buffers[buf_id].length) == -1) { - ERROR_LOG(Log::HLE, "munmap"); - goto exit; - } - } - - if (close(v4l_fd) == -1) { - ERROR_LOG(Log::HLE, "close"); - goto exit; - } - - v4l_fd = -1; - //pthread_join(v4l_thread, NULL); - -exit: - v4l_fd = -1; - return 0; -} - -#endif // PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) diff --git a/Core/HW/Camera.h b/Core/HW/Camera.h index 834ead10f6..ed1d58f71b 100644 --- a/Core/HW/Camera.h +++ b/Core/HW/Camera.h @@ -17,69 +17,5 @@ #pragma once #include "ppsspp_config.h" -#include "Core/HLE/sceUsbCam.h" - -#include "ext/jpge/jpgd.h" -#include "ext/jpge/jpge.h" - -extern "C" { -#ifdef USE_FFMPEG -#include "libswscale/swscale.h" -#include "libavutil/imgutils.h" -#endif //USE_FFMPEG -} void __cameraDummyImage(int width, int height, unsigned char** outData, int* outLen); - -#if defined(USING_QT_UI) -#include -#include - - class MyViewfinder : public QAbstractVideoSurface { - Q_OBJECT - public: - QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; - bool present(const QVideoFrame &frame); - }; - - static int qtc_ideal_width; - static int qtc_ideal_height; - static QCamera *qt_camera; - static QAbstractVideoSurface *qt_viewfinder; - - int __qt_startCapture(int width, int height); - int __qt_stopCapture(); - -#elif PPSSPP_PLATFORM(MAC) -std::vector __mac_getDeviceList(); -int __mac_startCapture(int width, int height); -int __mac_stopCapture(); -#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) -#include -#include -#include -#include - -#include "Common/Thread/ThreadUtil.h" - - typedef struct { - void *start; - int length; - } v4l_buf_t; - - static int v4l_fd = -1; - static uint32_t v4l_format; - static int v4l_hw_width; - static int v4l_hw_height; - static int v4l_height_fixed_aspect; - static int v4l_ideal_width; - static int v4l_ideal_height; - - static pthread_t v4l_thread; - static int v4l_buffer_count; - static v4l_buf_t *v4l_buffers; - - std::vector __v4l_getDeviceList(); - int __v4l_startCapture(int width, int height); - int __v4l_stopCapture(); -#endif diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 96476b2041..82ea66e4d3 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -6,9 +6,9 @@ // Currently supports: Android, Linux, Windows, Mac OSX #include "ppsspp_config.h" + #include #include -#include #include #include #include @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "ext/glslang/glslang/Public/ShaderLang.h" @@ -209,6 +211,21 @@ std::string System_GetProperty(SystemProperty prop) { } } +class MyViewfinder : public QAbstractVideoSurface { + Q_OBJECT +public: + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + bool present(const QVideoFrame &frame); +}; + +static int qtc_ideal_width; +static int qtc_ideal_height; +static QCamera *qt_camera; +static QAbstractVideoSurface *qt_viewfinder; + +int __qt_startCapture(int width, int height); +int __qt_stopCapture(); + std::vector __qt_getDeviceList() { std::vector deviceList; const QList cameras = QCameraInfo::availableCameras(); @@ -268,8 +285,10 @@ int __qt_startCapture(int width, int height) { return -1; } - char selectedCamera[80]; - sscanf(g_Config.sCameraDevice.c_str(), "%80s ", &selectedCamera[0]); + char selectedCamera[81]{}; + if (sscanf(g_Config.sCameraDevice.c_str(), "%80s ", &selectedCamera[0]) != 1) { + selectedCamera[0] = '\0'; + } const QList availableCameras = QCameraInfo::availableCameras(); if (availableCameras.size() < 1) { @@ -809,7 +828,13 @@ bool MainUI::event(QEvent *e) { NativeTouch(input); break; case QEvent::Wheel: - NativeKey(KeyInput(DEVICE_ID_MOUSE, ((QWheelEvent*)e)->delta()<0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KeyInputFlags::DOWN)); + { + const QPoint wheelDelta = ((QWheelEvent *)e)->angleDelta(); + const int delta = wheelDelta.y() != 0 ? wheelDelta.y() : wheelDelta.x(); + if (delta != 0) { + NativeKey(KeyInput(DEVICE_ID_MOUSE, delta < 0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KeyInputFlags::DOWN)); + } + } break; case QEvent::KeyPress: { @@ -994,9 +1019,6 @@ int main(int argc, char *argv[]) PROFILE_INIT(); glslang::InitializeProcess(); -#if defined(Q_OS_LINUX) - QApplication::setAttribute(Qt::AA_X11InitThreads, true); -#endif // Qt would otherwise default to a 3.0 compatibility profile // except on Nvidia, where Nvidia gives us the highest supported anyway @@ -1050,3 +1072,5 @@ int main(int argc, char *argv[]) glslang::FinalizeProcess(); return ret; } + +#include "QtMain.moc" diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index c499de440c..ae39a4f381 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -3,10 +3,10 @@ #include #include -#include #include #include #include +#include #include "mainwindow.h" @@ -395,11 +395,20 @@ void MainWindow::SetFullScreen(bool fullscreen) { if (GetUIState() == UISTATE_INGAME && QApplication::overrideCursor()) QApplication::restoreOverrideCursor(); - QDesktopWidget *desktop = QApplication::desktop(); int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); // Move window to the center of selected screen - QRect rect = desktop->screenGeometry(screenNum); + QRect rect; + const QList screens = QGuiApplication::screens(); + if (!screens.empty()) { + if (screenNum < 0) + screenNum = 0; + if (screenNum >= screens.size()) + screenNum = screens.size() - 1; + rect = screens[screenNum]->geometry(); + } else { + rect = QRect(QPoint(0, 0), size()); + } move((rect.width() - frameGeometry().width()) / 4, (rect.height() - frameGeometry().height()) / 4); } } diff --git a/SDL/MacCameraHelper.mm b/SDL/MacCameraHelper.mm index 597ed38c45..d2a7e89df9 100644 --- a/SDL/MacCameraHelper.mm +++ b/SDL/MacCameraHelper.mm @@ -13,6 +13,7 @@ #include "Common/Log.h" #include "Core/Config.h" #include "Core/HW/Camera.h" +#include "Core/HLE/sceUsbCam.h" namespace { diff --git a/SDL/SDLCocoaMetalLayer.mm b/SDL/SDLCocoaMetalLayer.mm index 539a91c7f8..921831de03 100644 --- a/SDL/SDLCocoaMetalLayer.mm +++ b/SDL/SDLCocoaMetalLayer.mm @@ -11,17 +11,18 @@ void *makeWindowMetalCompatible(void *window) { // https://github.com/KhronosGroup/MoltenVK/issues/78#issuecomment-371118536 #if PPSSPP_PLATFORM(MAC) - NSView *view = ((NSWindow *)window).contentView; + NSWindow *nsWindow = (__bridge NSWindow *)window; + NSView *view = nsWindow.contentView; assert([view isKindOfClass:[NSView class]]); if (![view.layer isKindOfClass:[CAMetalLayer class]]) { [view setLayer:[CAMetalLayer layer]]; - [[view layer] setContentsScale:[window backingScaleFactor]]; + [[view layer] setContentsScale:[nsWindow backingScaleFactor]]; } - return view.layer; + return (__bridge void *)view.layer; #else - UIView *view = (UIView *)window; + UIView *view = (__bridge UIView *)window; assert([view isKindOfClass:[UIView class]]); CAMetalLayer *metalLayer = [CAMetalLayer new]; @@ -33,6 +34,6 @@ void *makeWindowMetalCompatible(void *window) { metalLayer.drawableSize = viewSize; metalLayer.pixelFormat = (MTLPixelFormat)80;//BGRA8Unorm==80 [view.layer addSublayer:metalLayer]; - return metalLayer; + return (__bridge void *)metalLayer; #endif } diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index dbbe8454a8..9883262a6e 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -112,7 +112,374 @@ struct WindowState { }; static WindowState g_windowState; -int getDisplayNumber(void) { +#if PPSSPP_PLATFORM(MAC) + +// These are from MacCameraHelper.mm. +std::vector __mac_getDeviceList(); +int __mac_startCapture(int width, int height); +int __mac_stopCapture(); + +#endif + +#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + +#include "Core/HLE/sceUsbCam.h" + +#include "ext/jpge/jpgd.h" +#include "ext/jpge/jpge.h" + +extern "C" { +#ifdef USE_FFMPEG +#include "libswscale/swscale.h" +#include "libavutil/imgutils.h" +#endif //USE_FFMPEG +} + +#include +#include +#include +#include + +#include "Common/Thread/ThreadUtil.h" + +typedef struct { + void *start; + int length; +} v4l_buf_t; + +static int v4l_fd = -1; +static uint32_t v4l_format; +static int v4l_hw_width; +static int v4l_hw_height; +static int v4l_height_fixed_aspect; +static int v4l_ideal_width; +static int v4l_ideal_height; + +static pthread_t v4l_thread; +static int v4l_buffer_count; +static v4l_buf_t *v4l_buffers; + +std::vector __v4l_getDeviceList(); +int __v4l_startCapture(int width, int height); +int __v4l_stopCapture(); + + +#ifdef USE_FFMPEG +void convert_frame(int inw, int inh, unsigned char *inData, AVPixelFormat inFormat, + int outw, int outh, unsigned char **outData, int *outLen) { + struct SwsContext *sws_context = sws_getContext( + inw, inh, inFormat, + outw, outh, AV_PIX_FMT_RGB24, + SWS_BICUBIC, NULL, NULL, NULL); + + // resize + uint8_t *src[4] = {0}; + uint8_t *dst[4] = {0}; + int srcStride[4], dstStride[4]; + + unsigned char *rgbData = (unsigned char*)malloc(outw * outh * 4); + + av_image_fill_linesizes(srcStride, inFormat, inw); + av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, outw); + + av_image_fill_pointers(src, inFormat, inh, inData, srcStride); + av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, outh, rgbData, dstStride); + + sws_scale(sws_context, + src, srcStride, 0, inh, + dst, dstStride); + + // compress jpeg + *outLen = outw * outh * 2; + *outData = (unsigned char*)malloc(*outLen); + + jpge::params params; + params.m_quality = 60; + params.m_subsampling = jpge::H2V2; + params.m_two_pass_flag = false; + jpge::compress_image_to_jpeg_file_in_memory( + *outData, *outLen, outw, outh, 3, rgbData, params); + free(rgbData); +} +#endif //USE_FFMPEG + + + +#endif + +#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + +std::vector __v4l_getDeviceList() { + std::vector deviceList; +#ifdef USE_FFMPEG + for (int i = 0; i < 64; i++) { + char path[256]; + snprintf(path, sizeof(path), "/dev/video%d", i); + if (access(path, F_OK) < 0) { + break; + } + int fd = -1; + if((fd = open(path, O_RDONLY)) < 0) { + ERROR_LOG(Log::HLE, "Cannot open '%s'; errno=%d(%s)", path, errno, strerror(errno)); + continue; + } + struct v4l2_capability video_cap; + if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) < 0) { + ERROR_LOG(Log::HLE, "VIDIOC_QUERYCAP"); + goto cont; + } else { + char device[256]; + snprintf(device, sizeof(device), "%d:%s", i, video_cap.card); + deviceList.push_back(device); + } +cont: + close(fd); + fd = -1; + } +#endif //USE_FFMPEG + return deviceList; +} + +void *v4l_loop(void *data) { +#ifdef USE_FFMPEG + SetCurrentThreadName("v4l_loop"); + while (v4l_fd >= 0) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (ioctl(v4l_fd, VIDIOC_DQBUF, &buf) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_DQBUF; errno=%d(%s)", errno, strerror(errno)); + switch (errno) { + case EAGAIN: + continue; + default: + return nullptr; + } + } + + unsigned char *jpegData = nullptr; + int jpegLen = 0; + + if (v4l_format == V4L2_PIX_FMT_YUYV) { + convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)v4l_buffers[buf.index].start, AV_PIX_FMT_YUYV422, + v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); + } else if (v4l_format == V4L2_PIX_FMT_JPEG + || v4l_format == V4L2_PIX_FMT_MJPEG) { + // decompress jpeg + int width, height, req_comps; + unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory( + (unsigned char*)v4l_buffers[buf.index].start, buf.bytesused, &width, &height, &req_comps, 3); + + convert_frame(v4l_hw_width, v4l_hw_height, (unsigned char*)rgbData, AV_PIX_FMT_RGB24, + v4l_ideal_width, v4l_ideal_height, &jpegData, &jpegLen); + free(rgbData); + } + + if (jpegData) { + Camera::pushCameraImage(jpegLen, jpegData); + free(jpegData); + jpegData = nullptr; + } + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_QBUF"); + return nullptr; + } + } +#endif //USE_FFMPEG + return nullptr; +} + +int __v4l_startCapture(int ideal_width, int ideal_height) { +#ifdef USE_FFMPEG + if (v4l_fd >= 0) { + __v4l_stopCapture(); + } + v4l_ideal_width = ideal_width; + v4l_ideal_height = ideal_height; + + int dev_index = 0; + char dev_name[64]; + sscanf(g_Config.sCameraDevice.c_str(), "%d:", &dev_index); + snprintf(dev_name, sizeof(dev_name), "/dev/video%d", dev_index); + + if ((v4l_fd = open(dev_name, O_RDWR)) == -1) { + ERROR_LOG(Log::HLE, "Cannot open '%s'; errno=%d(%s)", dev_name, errno, strerror(errno)); + return -1; + } + + struct v4l2_capability cap; + memset(&cap, 0, sizeof(cap)); + if (ioctl(v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_QUERYCAP"); + return -1; + } + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + ERROR_LOG(Log::HLE, "V4L2_CAP_VIDEO_CAPTURE"); + return -1; + } + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + ERROR_LOG(Log::HLE, "V4L2_CAP_STREAMING"); + return -1; + } + + struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.pixelformat = 0; + + // select a pixel format + struct v4l2_fmtdesc desc; + memset(&desc, 0, sizeof(desc)); + desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while (ioctl(v4l_fd, VIDIOC_ENUM_FMT, &desc) == 0) { + desc.index++; + INFO_LOG(Log::HLE, "V4L2: pixel format supported: %s", desc.description); + if (fmt.fmt.pix.pixelformat != 0) { + continue; + } else if (desc.pixelformat == V4L2_PIX_FMT_YUYV + || desc.pixelformat == V4L2_PIX_FMT_JPEG + || desc.pixelformat == V4L2_PIX_FMT_MJPEG) { + INFO_LOG(Log::HLE, "V4L2: %s selected", desc.description); + fmt.fmt.pix.pixelformat = desc.pixelformat; + v4l_format = desc.pixelformat; + } + } + if (fmt.fmt.pix.pixelformat == 0) { + ERROR_LOG(Log::HLE, "V4L2: No supported format found"); + return -1; + } + + // select a frame size + fmt.fmt.pix.width = 0; + fmt.fmt.pix.height = 0; + struct v4l2_frmsizeenum frmsize; + memset(&frmsize, 0, sizeof(frmsize)); + frmsize.pixel_format = fmt.fmt.pix.pixelformat; + while (ioctl(v4l_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { + frmsize.index++; + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + INFO_LOG(Log::HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height); + bool matchesIdeal = frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height; + bool zeroPix = fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0; + bool pixLarger = frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height; + if (matchesIdeal && (zeroPix || pixLarger)) { + fmt.fmt.pix.width = frmsize.discrete.width; + fmt.fmt.pix.height = frmsize.discrete.height; + } + } + } + + if (fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0) { + fmt.fmt.pix.width = ideal_width; + fmt.fmt.pix.height = ideal_height; + } + INFO_LOG(Log::HLE, "V4L2: asking for %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height); + if (ioctl(v4l_fd, VIDIOC_S_FMT, &fmt) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_S_FMT"); + return -1; + } + v4l_hw_width = fmt.fmt.pix.width; + v4l_hw_height = fmt.fmt.pix.height; + INFO_LOG(Log::HLE, "V4L2: will receive %dx%d", v4l_hw_width, v4l_hw_height); + v4l_height_fixed_aspect = v4l_hw_width * ideal_height / ideal_width; + INFO_LOG(Log::HLE, "V4L2: will use %dx%d", v4l_hw_width, v4l_height_fixed_aspect); + + struct v4l2_requestbuffers req; + memset(&req, 0, sizeof(req)); + req.count = 1; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + if (ioctl(v4l_fd, VIDIOC_REQBUFS, &req) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_REQBUFS"); + return -1; + } + v4l_buffer_count = req.count; + INFO_LOG(Log::HLE, "V4L2: buffer count: %d", v4l_buffer_count); + v4l_buffers = (v4l_buf_t*) calloc(v4l_buffer_count, sizeof(v4l_buf_t)); + + for (int buf_id = 0; buf_id < v4l_buffer_count; buf_id++) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buf_id; + if (ioctl(v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_QUERYBUF"); + return -1; + } + + v4l_buffers[buf_id].length = buf.length; + v4l_buffers[buf_id].start = mmap(NULL, + buf.length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + v4l_fd, buf.m.offset); + if (v4l_buffers[buf_id].start == MAP_FAILED) { + ERROR_LOG(Log::HLE, "MAP_FAILED"); + return -1; + } + + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buf_id; + if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_QBUF"); + return -1; + } + } + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(v4l_fd, VIDIOC_STREAMON, &type) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_STREAMON"); + return -1; + } + + pthread_create(&v4l_thread, NULL, v4l_loop, NULL); +#endif //USE_FFMPEG + return 0; +} + +int __v4l_stopCapture() { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (v4l_fd < 0) { + goto exit; + } + + if (ioctl(v4l_fd, VIDIOC_STREAMOFF, &type) == -1) { + ERROR_LOG(Log::HLE, "VIDIOC_STREAMOFF"); + goto exit; + } + + for (int buf_id = 0; buf_id < v4l_buffer_count; buf_id++) { + if (munmap(v4l_buffers[buf_id].start, v4l_buffers[buf_id].length) == -1) { + ERROR_LOG(Log::HLE, "munmap"); + goto exit; + } + } + + if (close(v4l_fd) == -1) { + ERROR_LOG(Log::HLE, "close"); + goto exit; + } + + v4l_fd = -1; + //pthread_join(v4l_thread, NULL); + +exit: + v4l_fd = -1; + return 0; +} + +#endif // PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + +static int getDisplayNumber(void) { int displayNumber = 0; char * displayNumberStr; @@ -126,7 +493,7 @@ int getDisplayNumber(void) { return displayNumber; } -void sdl_mixaudio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) { +static void sdl_mixaudio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) { (void)total_amount; if (additional_amount <= 0) { return; @@ -505,18 +872,27 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string return true; } case SystemRequestType::CAMERA_COMMAND: -#if PPSSPP_PLATFORM(MAC) + { if (!strncmp(param1.c_str(), "startVideo", 10)) { int width = 0, height = 0; sscanf(param1.c_str(), "startVideo_%dx%d", &width, &height); +#if PPSSPP_PLATFORM(MAC) __mac_startCapture(width, height); +#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + __v4l_startCapture(width, height); +#endif } else if (!strcmp(param1.c_str(), "stopVideo")) { +#if PPSSPP_PLATFORM(MAC) __mac_stopCapture(); +#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) + __v4l_stopCapture(); +#endif + } else { + ERROR_LOG(Log::System, "Unknown camera command: %s", param1.c_str()); + return false; } return true; -#else - return false; -#endif + } case SystemRequestType::NOTIFY_UI_EVENT: { switch ((UIEventNotification)param3) { diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index a978ded604..f616904ab8 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -338,7 +338,7 @@ void RemoteISOScreen::CreateConnectTab(UI::ViewGroup *tab) { PermissionStatus status = System_GetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK); if (status != PERMISSION_STATUS_GRANTED) { // Proceed with local network functionality - tab->Add(new Choice(ri->T("Ask for network permission")))->OnClick.Add([this](UI::EventParams &e) { + tab->Add(new Choice(ri->T("Ask for network permission")))->OnClick.Add([](UI::EventParams &e) { System_AskForPermission(SYSTEM_PERMISSION_LOCAL_NETWORK); }); return; diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 0c48831c85..37efeb8c28 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -677,6 +677,11 @@ void System_LaunchUrl(LaunchUrlType urlType, std::string_view url) { } } +// Stub +std::vector System_GetCameraDeviceList() { + return std::vector(); +} + void System_Vibrate(int length_ms) { #if _M_ARM if (length_ms == -1 || length_ms == -3) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 76f7dc13aa..c3b13c0ec8 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -106,6 +106,7 @@ void System_Notify(SystemNotification notification) {} void System_PostUIMessage(UIMessage message, std::string_view param) {} void System_RunOnMainThread(std::function) {} +std::vector System_GetCameraDeviceList() { return std::vector(); } void System_AskForPermission(SystemPermission permission) {} PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } diff --git a/ios/IAPManager.mm b/ios/IAPManager.mm index c61c6e5f3a..e48a6025e0 100644 --- a/ios/IAPManager.mm +++ b/ios/IAPManager.mm @@ -6,6 +6,8 @@ #include "../ppsspp_config.h" +#if PPSSPP_PLATFORM(IOS_APP_STORE) + // Only one operation can be in progress at once. @implementation IAPManager { #ifdef USE_IAP @@ -229,3 +231,5 @@ static bool SafeStringEqual(NSString *a, NSString *b) { #endif @end + +#endif diff --git a/libretro/CMakeLists.txt b/libretro/CMakeLists.txt index d304b4dba4..f9fd9499fa 100644 --- a/libretro/CMakeLists.txt +++ b/libretro/CMakeLists.txt @@ -42,4 +42,7 @@ if(NOT MSVC) target_link_libraries(ppsspp_libretro "-Wl,-Bsymbolic") endif() endif() -target_link_libraries(ppsspp_libretro ${LinkCommon}) +target_link_libraries(ppsspp_libretro core ${CMAKE_THREAD_LIBS_INIT}) +if(ANDROID) + target_link_libraries(ppsspp_libretro log) +endif() diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 83c85fa72d..0130f4b93b 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -2025,15 +2025,11 @@ void System_AudioPushSamples(const int32_t *audio, int numSamples, float volume) void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } void System_AudioClear() {} #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) -std::vector System_GetCameraDeviceList() { return std::vector(); } bool System_AudioRecordingIsAvailable() { return false; } bool System_AudioRecordingState() { return false; } -#else -// Stub for now. -std::vector System_GetCameraDeviceList() { - return std::vector(); -} #endif +// Stub for now. +std::vector System_GetCameraDeviceList() { return std::vector(); } // TODO: To avoid having to define these here, these should probably be turned into system "requests". bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; } diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index cc2da2563e..85a8dd9b4a 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -127,6 +127,7 @@ void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples, float volume) {} bool System_SendDebugOutput(std::string_view data) { return false; } void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} +std::vector System_GetCameraDeviceList() { return std::vector(); } // Temporary hacks around annoying linking errors. Copied from Headless. void NativeFrame(GraphicsContext *graphicsContext) {}