From 8dc9ca5c81b296eb1a9f6a508db04f7440bf88b3 Mon Sep 17 00:00:00 2001 From: BtbN Date: Tue, 29 Apr 2014 02:59:53 +0200 Subject: [PATCH 1/2] Add xcomposite capture --- obs/window-basic-main.cpp | 1 + plugins/CMakeLists.txt | 1 + plugins/linux-xcomposite/CMakeLists.txt | 28 ++ plugins/linux-xcomposite/plugin-main.cpp | 101 ++++ plugins/linux-xcomposite/xcompcap-helper.cpp | 356 +++++++++++++++ plugins/linux-xcomposite/xcompcap-helper.h | 75 +++ plugins/linux-xcomposite/xcompcap-main.cpp | 457 +++++++++++++++++++ plugins/linux-xcomposite/xcompcap-main.h | 27 ++ 8 files changed, 1046 insertions(+) create mode 100644 plugins/linux-xcomposite/CMakeLists.txt create mode 100644 plugins/linux-xcomposite/plugin-main.cpp create mode 100644 plugins/linux-xcomposite/xcompcap-helper.cpp create mode 100644 plugins/linux-xcomposite/xcompcap-helper.h create mode 100644 plugins/linux-xcomposite/xcompcap-main.cpp create mode 100644 plugins/linux-xcomposite/xcompcap-main.h diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 79cc7a655..e09bf636e 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -445,6 +445,7 @@ void OBSBasic::OBSInit() obs_load_module("win-capture"); #else obs_load_module("linux-xshm"); + obs_load_module("linux-xcomposite"); obs_load_module("linux-pulseaudio"); #endif diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6d0c5f6c1..56b474076 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -9,6 +9,7 @@ elseif(APPLE) add_subdirectory(mac-capture) elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") add_subdirectory(linux-xshm) + add_subdirectory(linux-xcomposite) add_subdirectory(linux-pulseaudio) endif() diff --git a/plugins/linux-xcomposite/CMakeLists.txt b/plugins/linux-xcomposite/CMakeLists.txt new file mode 100644 index 000000000..2eca7286d --- /dev/null +++ b/plugins/linux-xcomposite/CMakeLists.txt @@ -0,0 +1,28 @@ +project(linux-xcomposite) + +find_package(X11) +if(NOT X11_Xcomposite_FOUND) + message(STATUS "Xcomposite library not found, Xcomposite plugin disabled") + return() +endif() + +include_directories( + ${X11_Xcomposite_INCLUDE_PATH} + ${X11_X11_INCLUDE_PATH}) + +set(linux-xcomposite_SOURCES + plugin-main.cpp + xcompcap-main.cpp + xcompcap-main.h + xcompcap-helper.cpp + xcompcap-helper.h) + +add_library(linux-xcomposite MODULE + ${linux-xcomposite_SOURCES}) +target_link_libraries(linux-xcomposite + libobs + glad + ${X11_X11_LIB} + ${X11_Xcomposite_LIB}) + +install_obs_plugin(linux-xcomposite) diff --git a/plugins/linux-xcomposite/plugin-main.cpp b/plugins/linux-xcomposite/plugin-main.cpp new file mode 100644 index 000000000..53e3099df --- /dev/null +++ b/plugins/linux-xcomposite/plugin-main.cpp @@ -0,0 +1,101 @@ +#include + +#include "xcompcap-main.h" + +static void* xcompcap_create(obs_data_t settings, obs_source_t source) +{ + return new XCompcapMain(settings, source); +} + +static void xcompcap_destroy(void *data) +{ + XCompcapMain* cc = (XCompcapMain*)data; + delete cc; +} + +static void xcompcap_video_tick(void* data, float seconds) +{ + XCompcapMain* cc = (XCompcapMain*)data; + cc->tick(seconds); +} + +static void xcompcap_video_render(void* data, effect_t effect) +{ + XCompcapMain* cc = (XCompcapMain*)data; + cc->render(effect); +} + +static uint32_t xcompcap_getwidth(void* data) +{ + XCompcapMain* cc = (XCompcapMain*)data; + return cc->width(); +} + +static uint32_t xcompcap_getheight(void* data) +{ + XCompcapMain* cc = (XCompcapMain*)data; + return cc->height(); +} + +static obs_properties_t xcompcap_props(const char *locale) +{ + return XCompcapMain::properties(locale); +} + +void xcompcap_defaults(obs_data_t settings) +{ + XCompcapMain::defaults(settings); +} + +void xcompcap_update(void *data, obs_data_t settings) +{ + XCompcapMain* cc = (XCompcapMain*)data; + cc->updateSettings(settings); +} + +OBS_DECLARE_MODULE() + +static const char* xcompcap_getname(const char* locale) +{ + UNUSED_PARAMETER(locale); + + return "Xcomposite capture"; +} + +bool obs_module_load(uint32_t libobs_version) +{ + UNUSED_PARAMETER(libobs_version); + + if(!XCompcapMain::init()) + return false; + + obs_source_info sinfo; + memset(&sinfo, 0, sizeof(obs_source_info)); + + sinfo.id = "xcomposite_input"; + sinfo.output_flags = OBS_SOURCE_VIDEO; + + sinfo.getname = xcompcap_getname; + sinfo.create = xcompcap_create; + sinfo.destroy = xcompcap_destroy; + sinfo.properties = xcompcap_props; + sinfo.defaults = xcompcap_defaults; + sinfo.update = xcompcap_update; + sinfo.video_tick = xcompcap_video_tick; + sinfo.video_render = xcompcap_video_render; + sinfo.getwidth = xcompcap_getwidth; + sinfo.getheight = xcompcap_getheight; + + obs_register_source(&sinfo); + + blog(LOG_INFO, "Xcomposite capture plugin loaded"); + + return true; +} + +void obs_module_unload() +{ + XCompcapMain::deinit(); + + blog(LOG_INFO, "Xcomposite capture plugin unloaded"); +} diff --git a/plugins/linux-xcomposite/xcompcap-helper.cpp b/plugins/linux-xcomposite/xcompcap-helper.cpp new file mode 100644 index 000000000..008c6c1a8 --- /dev/null +++ b/plugins/linux-xcomposite/xcompcap-helper.cpp @@ -0,0 +1,356 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#include "xcompcap-helper.h" + +namespace XCompcap +{ + static Display* xdisplay = 0; + + Display *disp() + { + if(!xdisplay) + xdisplay = XOpenDisplay(NULL); + + return xdisplay; + } + + void cleanupDisplay() + { + if(!xdisplay) + return; + + XCloseDisplay(xdisplay); + xdisplay = 0; + } + + static void getAllWindows(Window parent, std::list& windows) + { + UNUSED_PARAMETER(parent); + UNUSED_PARAMETER(windows); + } + + std::list getAllWindows() + { + std::list res; + + for(int i = 0; i < ScreenCount(disp()); ++i) + getAllWindows(RootWindow(disp(), i), res); + + return res; + } + + std::list getTopLevelWindows() + { + std::list res; + + Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true); + Atom actualType; + int format; + unsigned long num, bytes; + Window* data = 0; + + for(int i = 0; i < ScreenCount(disp()); ++i) + { + Window rootWin = RootWindow(disp(), i); + + int status = XGetWindowProperty( + disp(), + rootWin, + netClList, + 0L, + ~0L, + false, + AnyPropertyType, + &actualType, + &format, + &num, + &bytes, + (uint8_t**)&data); + + if(status != Success) + { + blog(LOG_WARNING, "Failed getting root window properties"); + continue; + } + + for(unsigned long i = 0; i < num; ++i) + { + res.push_back(data[i]); + } + + XFree(data); + } + + return res; + } + + int getRootWindowScreen(Window root) + { + XWindowAttributes attr; + + if(!XGetWindowAttributes(disp(), root, &attr)) + return DefaultScreen(disp()); + + return XScreenNumberOfScreen(attr.screen); + } + + std::string getWindowName(Window win) + { + Atom netWmName = XInternAtom(disp(), "_NET_WM_NAME", false); + int n; + char **list = 0; + XTextProperty tp; + std::string res = "unknown"; + + XGetTextProperty(disp(), win, &tp, netWmName); + + if(!tp.nitems) + XGetWMName(disp(), win, &tp); + + if(!tp.nitems) + return "error"; + + if(tp.encoding == XA_STRING) + { + res = (char*)tp.value; + } + else if(XmbTextPropertyToTextList(disp(), &tp, &list, &n) >= Success && n > 0 && *list) + { + res = *list; + XFreeStringList(list); + } + + XFree(tp.value); + + return res; + } + + std::string getWindowCommand(Window win) + { + Atom xi = XInternAtom(disp(), "WM_COMMAND", false); + int n; + char **list = 0; + XTextProperty tp; + std::string res = "error"; + + XGetTextProperty(disp(), win, &tp, xi); + + if(!tp.nitems) + return std::string(); + + if(tp.encoding == XA_STRING) + { + res = (char*)tp.value; + } + else if(XmbTextPropertyToTextList(disp(), &tp, &list, &n) >= Success && n > 0 && *list) + { + res = *list; + XFreeStringList(list); + } + + XFree(tp.value); + + return res; + } + + int getWindowPid(Window win) + { + UNUSED_PARAMETER(win); + return 1234; //TODO + } + + static std::unordered_set changedWindows; + static pthread_mutex_t changeLock = PTHREAD_MUTEX_INITIALIZER; + void processEvents() + { + PLock lock(&changeLock); + + XLockDisplay(disp()); + + while(XEventsQueued(disp(), QueuedAfterReading) > 0) + { + XEvent ev; + + XNextEvent(disp(), &ev); + + if(ev.type == ConfigureNotify) + { + changedWindows.insert(ev.xconfigure.event); + } + + if(ev.type == MapNotify) + { + changedWindows.insert(ev.xmap.event); + } + + if(ev.type == DestroyNotify) + { + changedWindows.insert(ev.xdestroywindow.event); + } + } + + XUnlockDisplay(disp()); + } + + bool windowWasReconfigured(Window win) + { + PLock lock(&changeLock); + + auto it = changedWindows.find(win); + if(it != changedWindows.end()) + { + changedWindows.erase(it); + return true; + } + + return false; + } + +} + + +PLock::PLock(pthread_mutex_t* mtx, bool trylock) + :m(mtx) +{ + if(trylock) + islock = mtx && pthread_mutex_trylock(mtx) == 0; + else + islock = mtx && pthread_mutex_lock(mtx) == 0; +} + +PLock::~PLock() +{ + if(islock) + { + pthread_mutex_unlock(m); + } +} + +bool PLock::isLocked() +{ + return islock; +} + +void PLock::unlock() +{ + if(islock) + { + pthread_mutex_unlock(m); + islock = false; + } +} + +void PLock::lock() +{ + if(!islock) + { + pthread_mutex_lock(m); + islock = true; + } +} + + + +static bool* curErrorTarget = 0; +static char curErrorText[200]; +static int xerrorlock_handler(Display* disp, XErrorEvent* err) +{ + + if(curErrorTarget) + *curErrorTarget = true; + + XGetErrorText(disp, err->error_code, curErrorText, 200); + + return 0; +} + +XErrorLock::XErrorLock() +{ + goterr = false; + islock = false; + prevhandler = 0; + + lock(); +} + +XErrorLock::~XErrorLock() +{ + unlock(); +} + +bool XErrorLock::isLocked() +{ + return islock; +} + +void XErrorLock::lock() +{ + if(!islock) + { + XLockDisplay(XCompcap::disp()); + XSync(XCompcap::disp(), 0); + + curErrorTarget = &goterr; + curErrorText[0] = 0; + prevhandler = XSetErrorHandler(xerrorlock_handler); + + islock = true; + } +} + +void XErrorLock::unlock() +{ + if(islock) + { + curErrorTarget = 0; + XSetErrorHandler(prevhandler); + prevhandler = 0; + XUnlockDisplay(XCompcap::disp()); + islock = false; + } +} + +bool XErrorLock::gotError() +{ + if(!islock) + return false; + + XSync(XCompcap::disp(), 0); + + bool res = goterr; + goterr = false; + return res; +} + +std::string XErrorLock::getErrorText() +{ + return curErrorText; +} + +void XErrorLock::resetError() +{ + if(islock) + XSync(XCompcap::disp(), 0); + + goterr = false; + curErrorText[0] = 0; +} + + +ObsGsContextHolder::ObsGsContextHolder() +{ + gs_entercontext(obs_graphics()); +} + +ObsGsContextHolder::~ObsGsContextHolder() +{ + gs_leavecontext(); +} diff --git a/plugins/linux-xcomposite/xcompcap-helper.h b/plugins/linux-xcomposite/xcompcap-helper.h new file mode 100644 index 000000000..d4b008f43 --- /dev/null +++ b/plugins/linux-xcomposite/xcompcap-helper.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#define blog(level, msg, ...) blog(level, "xcompcap: " msg, ##__VA_ARGS__) + + +class PLock +{ + pthread_mutex_t *m; + bool islock; + + public: + PLock(const PLock&) = delete; + PLock& operator=(const PLock&) = delete; + + PLock(pthread_mutex_t* mtx, bool trylock = false); + + ~PLock(); + + bool isLocked(); + + void unlock(); + void lock(); +}; + +class XErrorLock +{ + bool islock; + bool goterr; + XErrorHandler prevhandler; + + public: + XErrorLock(const XErrorLock&) = delete; + XErrorLock& operator=(const XErrorLock&) = delete; + + XErrorLock(); + ~XErrorLock(); + + bool isLocked(); + + void unlock(); + void lock(); + + bool gotError(); + std::string getErrorText(); + void resetError(); +}; + +class ObsGsContextHolder +{ + public: + ObsGsContextHolder(const ObsGsContextHolder&) = delete; + ObsGsContextHolder& operator=(const ObsGsContextHolder&) = delete; + + ObsGsContextHolder(); + ~ObsGsContextHolder(); +}; + +namespace XCompcap +{ + Display* disp(); + void cleanupDisplay(); + + std::string getWindowCommand(Window win); + int getRootWindowScreen(Window root); + std::string getWindowName(Window win); + int getWindowPid(Window win); + std::list getTopLevelWindows(); + std::list getAllWindows(); + + void processEvents(); + bool windowWasReconfigured(Window win); +} diff --git a/plugins/linux-xcomposite/xcompcap-main.cpp b/plugins/linux-xcomposite/xcompcap-main.cpp new file mode 100644 index 000000000..70df43362 --- /dev/null +++ b/plugins/linux-xcomposite/xcompcap-main.cpp @@ -0,0 +1,457 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "xcompcap-main.h" +#include "xcompcap-helper.h" + +#define xdisp (XCompcap::disp()) +#define WIN_STRING_DIV "\r\n" + +bool XCompcapMain::init() +{ + if(!xdisp) + { + blog(LOG_ERROR, "failed opening display"); + return false; + } + + int eventBase, errorBase; + if(!XCompositeQueryExtension(xdisp, &eventBase, &errorBase)) + { + blog(LOG_ERROR, "Xcomposite extension not supported"); + return false; + } + + int major = 0, minor = 2; + XCompositeQueryVersion(xdisp, &major, &minor); + + if(major == 0 && minor < 2) + { + blog(LOG_ERROR, "Xcomposite extension is too old: %d.%d < 0.2", major, minor); + return false; + } + + return true; +} + +void XCompcapMain::deinit() +{ + XCompcap::cleanupDisplay(); +} + +obs_properties_t XCompcapMain::properties(const char *locale) +{ + obs_properties_t props = obs_properties_create(locale); + + obs_property_t wins = obs_properties_add_list(props, "capture_window", "Captured Window", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + + for(Window win: XCompcap::getTopLevelWindows()) + { + std::string wname = XCompcap::getWindowName(win); + std::string progpath = XCompcap::getWindowCommand(win); + std::string winid = std::to_string((long long)win); + obs_property_list_add_string(wins, wname.c_str(), (winid + WIN_STRING_DIV + wname + WIN_STRING_DIV + progpath).c_str()); + } + + obs_properties_add_int(props, "cut_top", "Cut top pixels", 0, 4096, 1); + obs_properties_add_int(props, "cut_left", "Cut left pixels", 0, 4096, 1); + obs_properties_add_int(props, "cut_right", "Cut right pixels", 0, 4096, 1); + obs_properties_add_int(props, "cut_bot", "Cut bottom pixels", 0, 4096, 1); + + obs_properties_add_bool(props, "swap_redblue", "Swap red and blue"); + obs_properties_add_bool(props, "lock_x", "Lock X server when capturing"); + + return props; +} + +void XCompcapMain::defaults(obs_data_t settings) +{ + obs_data_setstring(settings, "capture_window", ""); + obs_data_setint(settings, "cut_top", 0); + obs_data_setint(settings, "cut_left", 0); + obs_data_setint(settings, "cut_right", 0); + obs_data_setint(settings, "cut_bot", 0); + obs_data_setbool(settings, "swap_redblue", false); + obs_data_setbool(settings, "lock_x", false); +} + + +struct XCompcapMain_private +{ + XCompcapMain_private() + :win(0) + ,cut_top(0),cur_cut_top(0) + ,cut_left(0),cur_cut_left(0) + ,cut_right(0),cur_cut_right(0) + ,cut_bot(0),cur_cut_bot(0) + ,inverted(false) + ,width(0),height(0) + ,pixmap(0) + ,glxpixmap(0) + ,tex(0) + ,gltex(0) + { + pthread_mutexattr_init(&lockattr); + pthread_mutexattr_settype(&lockattr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&lock, &lockattr); + } + + ~XCompcapMain_private() + { + pthread_mutex_destroy(&lock); + pthread_mutexattr_destroy(&lockattr); + } + + obs_source_t source; + + Window win; + int cut_top, cur_cut_top; + int cut_left, cur_cut_left; + int cut_right, cur_cut_right; + int cut_bot, cur_cut_bot; + bool inverted; + bool swapRedBlue; + bool lockX; + + uint32_t width; + uint32_t height; + + Pixmap pixmap; + GLXPixmap glxpixmap; + texture_t tex; + texture_t gltex; + + pthread_mutex_t lock; + pthread_mutexattr_t lockattr; +}; + + +XCompcapMain::XCompcapMain(obs_data_t settings, obs_source_t source) +{ + p = new XCompcapMain_private; + p->source = source; + + updateSettings(settings); +} + +static void xcc_cleanup(XCompcapMain_private *p); + +XCompcapMain::~XCompcapMain() +{ + ObsGsContextHolder obsctx; + + if(p->tex) + { + texture_destroy(p->tex); + p->tex = 0; + } + + xcc_cleanup(p); + + delete p; +} + +static Window getWindowFromString(std::string wstr) +{ + if(wstr == "") + { + return XCompcap::getTopLevelWindows().front(); + } + + if(wstr.substr(0, 4) == "root") + { + int i = std::stoi("0" + wstr.substr(4)); + return RootWindow(xdisp, i); + } + + size_t firstMark = wstr.find(WIN_STRING_DIV); + + if(firstMark == std::string::npos) + return (Window)std::stol(wstr); + + std::string widstr = wstr.substr(0, firstMark); + Window wid = (Window)std::stol(widstr); + + wstr = wstr.substr(firstMark + strlen(WIN_STRING_DIV)); + + size_t lastMark = wstr.rfind(WIN_STRING_DIV); + std::string wname = wstr.substr(0, lastMark); + + Window matchedNameWin = wid; + for(Window cwin: XCompcap::getTopLevelWindows()) + { + std::string cwinname = XCompcap::getWindowName(cwin); + + if(cwin == wid && wname == cwinname) + return wid; + + if(wname == cwinname) + matchedNameWin = cwin; + } + + return matchedNameWin; +} + +static void xcc_cleanup(XCompcapMain_private *p) +{ + if(p->gltex) + { + texture_destroy(p->gltex); + p->gltex = 0; + } + + if(p->glxpixmap) + { + glXDestroyPixmap(xdisp, p->glxpixmap); + p->glxpixmap = 0; + } + + if(p->pixmap) + { + XFreePixmap(xdisp, p->pixmap); + p->pixmap = 0; + } + + if(p->win) + { + XCompositeUnredirectWindow(xdisp, p->win, CompositeRedirectAutomatic); + XSelectInput(xdisp, p->win, 0); + p->win = 0; + } +} + +void XCompcapMain::updateSettings(obs_data_t settings) +{ + PLock lock(&p->lock); + XErrorLock xlock; + ObsGsContextHolder obsctx; + + blog(LOG_DEBUG, "Settings updating"); + + Window prevWin = p->win; + + xcc_cleanup(p); + + if(settings) + { + p->win = getWindowFromString(obs_data_getstring(settings, "capture_window")); + + p->cut_top = obs_data_getint(settings, "cut_top"); + p->cut_left = obs_data_getint(settings, "cut_left"); + p->cut_right = obs_data_getint(settings, "cut_right"); + p->cut_bot = obs_data_getint(settings, "cut_bot"); + p->lockX = obs_data_getbool(settings, "lock_x"); + p->swapRedBlue = obs_data_getbool(settings, "swap_redblue"); + } + else + { + p->win = prevWin; + } + + xlock.resetError(); + + XCompositeRedirectWindow(xdisp, p->win, CompositeRedirectAutomatic); + + if(xlock.gotError()) + { + blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s", xlock.getErrorText().c_str()); + return; + } + + XSelectInput(xdisp, p->win, StructureNotifyMask); + XSync(xdisp, 0); + + XWindowAttributes attr; + if(!XGetWindowAttributes(xdisp, p->win, &attr)) + { + p->win = 0; + p->width = 0; + p->height = 0; + return; + } + + gs_color_format cf = GS_RGBA; + + p->width = attr.width; + p->height = attr.height; + + if(p->cut_top + p->cut_bot < (int)p->height) + { + p->cur_cut_top = p->cut_top; + p->cur_cut_bot = p->cut_bot; + } + else + { + p->cur_cut_top = 0; + p->cur_cut_bot = 0; + } + + if(p->cut_left + p->cut_right < (int)p->width) + { + p->cur_cut_left = p->cut_left; + p->cur_cut_right = p->cut_right; + } + else + { + p->cur_cut_left = 0; + p->cur_cut_right = 0; + } + + if(p->tex) + texture_destroy(p->tex); + + uint8_t *texData = new uint8_t[width() * height() * 4]; + + for(unsigned int i = 0; i < width() * height() * 4; i += 4) + { + texData[i + 0] = p->swapRedBlue ? 0xFF : 0; + texData[i + 1] = 0; + texData[i + 2] = p->swapRedBlue ? 0 : 0xFF; + texData[i + 3] = 0xFF; + } + + const uint8_t* texDataArr[] = { texData, 0 }; + + p->tex = gs_create_texture(width(), height(), cf, 1, (const void**)texDataArr, 0); + + delete[] texData; + + if(!p->swapRedBlue) + { + GLuint tex = *(GLuint*)texture_getobj(p->tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glBindTexture(GL_TEXTURE_2D, 0); + } + + const int attrs[] = + { + GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + GLX_DOUBLEBUFFER, GL_FALSE, + None + }; + + int nelem = 0; + GLXFBConfig* configs = glXChooseFBConfig(xdisp, XCompcap::getRootWindowScreen(attr.root), attrs, &nelem); + + if(nelem <= 0) + { + blog(LOG_ERROR, "no matching fb config found"); + p->win = 0; + p->height = 0; + p->width = 0; + return; + } + + glXGetFBConfigAttrib(xdisp, configs[0], GLX_Y_INVERTED_EXT, &nelem); + p->inverted = nelem != 0; + + xlock.resetError(); + + p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win); + + if(xlock.gotError()) + { + blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s", xlock.getErrorText().c_str()); + p->pixmap = 0; + XFree(configs); + return; + } + + const int attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, + None }; + p->glxpixmap = glXCreatePixmap(xdisp, configs[0], p->pixmap, attribs); + + if(xlock.gotError()) + { + blog(LOG_ERROR, "glXCreatePixmap failed: %s", xlock.getErrorText().c_str()); + XFreePixmap(xdisp, p->pixmap); + XFree(configs); + p->pixmap = 0; + p->glxpixmap = 0; + return; + } + + XFree(configs); + + p->gltex = gs_create_texture(p->width, p->height, cf, 1, 0, GS_GL_DUMMYTEX); + + GLuint gltex = *(GLuint*)texture_getobj(p->gltex); + glBindTexture(GL_TEXTURE_2D, gltex); + glXBindTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +void XCompcapMain::tick(float seconds) +{ + UNUSED_PARAMETER(seconds); + + PLock lock(&p->lock, true); + + if(!lock.isLocked()) + return; + + XCompcap::processEvents(); + + if(XCompcap::windowWasReconfigured(p->win)) + updateSettings(0); + + if(!p->tex || !p->gltex) + return; + + gs_entercontext(obs_graphics()); + + if(p->lockX) + { + XLockDisplay(xdisp); + XSync(xdisp, 0); + } + + gs_copy_texture_region(p->tex, 0, 0, p->gltex, p->cur_cut_left, p->cur_cut_top, width(), height()); + + if(p->lockX) + { + XUnlockDisplay(xdisp); + } + + gs_leavecontext(); +} + +void XCompcapMain::render(effect_t effect) +{ + PLock lock(&p->lock, true); + + if(!lock.isLocked() || !p->tex) + return; + + eparam_t image = effect_getparambyname(effect, "image"); + effect_settexture(effect, image, p->tex); + + gs_enable_blending(false); + gs_draw_sprite(p->tex, 0, 0, 0); +} + +uint32_t XCompcapMain::width() +{ + return p->width - p->cur_cut_left - p->cur_cut_right; +} + +uint32_t XCompcapMain::height() +{ + return p->height - p->cur_cut_bot - p->cur_cut_top; +} diff --git a/plugins/linux-xcomposite/xcompcap-main.h b/plugins/linux-xcomposite/xcompcap-main.h new file mode 100644 index 000000000..8d231764a --- /dev/null +++ b/plugins/linux-xcomposite/xcompcap-main.h @@ -0,0 +1,27 @@ +#pragma once + +struct XCompcapMain_private; + +class XCompcapMain +{ + public: + static bool init(); + static void deinit(); + + static obs_properties_t properties(const char *locale); + static void defaults(obs_data_t settings); + + XCompcapMain(obs_data_t settings, obs_source_t source); + ~XCompcapMain(); + + void updateSettings(obs_data_t settings); + + void tick(float seconds); + void render(effect_t effect); + + uint32_t width(); + uint32_t height(); + + private: + XCompcapMain_private *p; +}; From 3280cd1dbb44433ebbfcfa929c31cf8865428616 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 15 May 2014 02:12:22 -0700 Subject: [PATCH 2/2] Fix code formatting issues 80 columns limit preferably spaces after if/for/while KNF code styling --- plugins/linux-xcomposite/plugin-main.cpp | 2 +- plugins/linux-xcomposite/xcompcap-helper.cpp | 102 +++++------ plugins/linux-xcomposite/xcompcap-main.cpp | 178 +++++++++---------- 3 files changed, 134 insertions(+), 148 deletions(-) diff --git a/plugins/linux-xcomposite/plugin-main.cpp b/plugins/linux-xcomposite/plugin-main.cpp index 53e3099df..cd814852b 100644 --- a/plugins/linux-xcomposite/plugin-main.cpp +++ b/plugins/linux-xcomposite/plugin-main.cpp @@ -66,7 +66,7 @@ bool obs_module_load(uint32_t libobs_version) { UNUSED_PARAMETER(libobs_version); - if(!XCompcapMain::init()) + if (!XCompcapMain::init()) return false; obs_source_info sinfo; diff --git a/plugins/linux-xcomposite/xcompcap-helper.cpp b/plugins/linux-xcomposite/xcompcap-helper.cpp index 008c6c1a8..ef24baa06 100644 --- a/plugins/linux-xcomposite/xcompcap-helper.cpp +++ b/plugins/linux-xcomposite/xcompcap-helper.cpp @@ -16,7 +16,7 @@ namespace XCompcap Display *disp() { - if(!xdisplay) + if (!xdisplay) xdisplay = XOpenDisplay(NULL); return xdisplay; @@ -24,7 +24,7 @@ namespace XCompcap void cleanupDisplay() { - if(!xdisplay) + if (!xdisplay) return; XCloseDisplay(xdisplay); @@ -41,7 +41,7 @@ namespace XCompcap { std::list res; - for(int i = 0; i < ScreenCount(disp()); ++i) + for (int i = 0; i < ScreenCount(disp()); ++i) getAllWindows(RootWindow(disp(), i), res); return res; @@ -51,14 +51,13 @@ namespace XCompcap { std::list res; - Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true); + Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true); Atom actualType; int format; unsigned long num, bytes; Window* data = 0; - for(int i = 0; i < ScreenCount(disp()); ++i) - { + for (int i = 0; i < ScreenCount(disp()); ++i) { Window rootWin = RootWindow(disp(), i); int status = XGetWindowProperty( @@ -75,16 +74,14 @@ namespace XCompcap &bytes, (uint8_t**)&data); - if(status != Success) - { - blog(LOG_WARNING, "Failed getting root window properties"); + if (status != Success) { + blog(LOG_WARNING, "Failed getting root " + "window properties"); continue; } - for(unsigned long i = 0; i < num; ++i) - { + for (unsigned long i = 0; i < num; ++i) res.push_back(data[i]); - } XFree(data); } @@ -96,7 +93,7 @@ namespace XCompcap { XWindowAttributes attr; - if(!XGetWindowAttributes(disp(), root, &attr)) + if (!XGetWindowAttributes(disp(), root, &attr)) return DefaultScreen(disp()); return XScreenNumberOfScreen(attr.screen); @@ -112,20 +109,22 @@ namespace XCompcap XGetTextProperty(disp(), win, &tp, netWmName); - if(!tp.nitems) + if (!tp.nitems) XGetWMName(disp(), win, &tp); - if(!tp.nitems) + if (!tp.nitems) return "error"; - if(tp.encoding == XA_STRING) - { + if (tp.encoding == XA_STRING) { res = (char*)tp.value; - } - else if(XmbTextPropertyToTextList(disp(), &tp, &list, &n) >= Success && n > 0 && *list) - { - res = *list; - XFreeStringList(list); + } else { + int ret = XmbTextPropertyToTextList(disp(), &tp, &list, + &n); + + if (ret >= Success && n > 0 && *list) { + res = *list; + XFreeStringList(list); + } } XFree(tp.value); @@ -143,17 +142,18 @@ namespace XCompcap XGetTextProperty(disp(), win, &tp, xi); - if(!tp.nitems) + if (!tp.nitems) return std::string(); - if(tp.encoding == XA_STRING) - { + if (tp.encoding == XA_STRING) { res = (char*)tp.value; - } - else if(XmbTextPropertyToTextList(disp(), &tp, &list, &n) >= Success && n > 0 && *list) - { - res = *list; - XFreeStringList(list); + } else { + int ret = XmbTextPropertyToTextList(disp(), &tp, &list, + &n); + if (ret >= Success && n > 0 && *list) { + res = *list; + XFreeStringList(list); + } } XFree(tp.value); @@ -175,26 +175,19 @@ namespace XCompcap XLockDisplay(disp()); - while(XEventsQueued(disp(), QueuedAfterReading) > 0) - { + while (XEventsQueued(disp(), QueuedAfterReading) > 0) { XEvent ev; XNextEvent(disp(), &ev); - if(ev.type == ConfigureNotify) - { + if (ev.type == ConfigureNotify) changedWindows.insert(ev.xconfigure.event); - } - if(ev.type == MapNotify) - { + if (ev.type == MapNotify) changedWindows.insert(ev.xmap.event); - } - if(ev.type == DestroyNotify) - { + if (ev.type == DestroyNotify) changedWindows.insert(ev.xdestroywindow.event); - } } XUnlockDisplay(disp()); @@ -205,8 +198,8 @@ namespace XCompcap PLock lock(&changeLock); auto it = changedWindows.find(win); - if(it != changedWindows.end()) - { + + if (it != changedWindows.end()) { changedWindows.erase(it); return true; } @@ -220,7 +213,7 @@ namespace XCompcap PLock::PLock(pthread_mutex_t* mtx, bool trylock) :m(mtx) { - if(trylock) + if (trylock) islock = mtx && pthread_mutex_trylock(mtx) == 0; else islock = mtx && pthread_mutex_lock(mtx) == 0; @@ -228,8 +221,7 @@ PLock::PLock(pthread_mutex_t* mtx, bool trylock) PLock::~PLock() { - if(islock) - { + if (islock) { pthread_mutex_unlock(m); } } @@ -241,8 +233,7 @@ bool PLock::isLocked() void PLock::unlock() { - if(islock) - { + if (islock) { pthread_mutex_unlock(m); islock = false; } @@ -250,8 +241,7 @@ void PLock::unlock() void PLock::lock() { - if(!islock) - { + if (!islock) { pthread_mutex_lock(m); islock = true; } @@ -264,7 +254,7 @@ static char curErrorText[200]; static int xerrorlock_handler(Display* disp, XErrorEvent* err) { - if(curErrorTarget) + if (curErrorTarget) *curErrorTarget = true; XGetErrorText(disp, err->error_code, curErrorText, 200); @@ -293,8 +283,7 @@ bool XErrorLock::isLocked() void XErrorLock::lock() { - if(!islock) - { + if (!islock) { XLockDisplay(XCompcap::disp()); XSync(XCompcap::disp(), 0); @@ -308,8 +297,7 @@ void XErrorLock::lock() void XErrorLock::unlock() { - if(islock) - { + if (islock) { curErrorTarget = 0; XSetErrorHandler(prevhandler); prevhandler = 0; @@ -320,7 +308,7 @@ void XErrorLock::unlock() bool XErrorLock::gotError() { - if(!islock) + if (!islock) return false; XSync(XCompcap::disp(), 0); @@ -337,7 +325,7 @@ std::string XErrorLock::getErrorText() void XErrorLock::resetError() { - if(islock) + if (islock) XSync(XCompcap::disp(), 0); goterr = false; diff --git a/plugins/linux-xcomposite/xcompcap-main.cpp b/plugins/linux-xcomposite/xcompcap-main.cpp index 70df43362..bd4b42b42 100644 --- a/plugins/linux-xcomposite/xcompcap-main.cpp +++ b/plugins/linux-xcomposite/xcompcap-main.cpp @@ -18,15 +18,13 @@ bool XCompcapMain::init() { - if(!xdisp) - { + if (!xdisp) { blog(LOG_ERROR, "failed opening display"); return false; } int eventBase, errorBase; - if(!XCompositeQueryExtension(xdisp, &eventBase, &errorBase)) - { + if (!XCompositeQueryExtension(xdisp, &eventBase, &errorBase)) { blog(LOG_ERROR, "Xcomposite extension not supported"); return false; } @@ -34,9 +32,9 @@ bool XCompcapMain::init() int major = 0, minor = 2; XCompositeQueryVersion(xdisp, &major, &minor); - if(major == 0 && minor < 2) - { - blog(LOG_ERROR, "Xcomposite extension is too old: %d.%d < 0.2", major, minor); + if (major == 0 && minor < 2) { + blog(LOG_ERROR, "Xcomposite extension is too old: %d.%d < 0.2", + major, minor); return false; } @@ -52,23 +50,34 @@ obs_properties_t XCompcapMain::properties(const char *locale) { obs_properties_t props = obs_properties_create(locale); - obs_property_t wins = obs_properties_add_list(props, "capture_window", "Captured Window", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_t wins = obs_properties_add_list(props, "capture_window", + "Captured Window", OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); - for(Window win: XCompcap::getTopLevelWindows()) - { + for (Window win: XCompcap::getTopLevelWindows()) { std::string wname = XCompcap::getWindowName(win); std::string progpath = XCompcap::getWindowCommand(win); std::string winid = std::to_string((long long)win); - obs_property_list_add_string(wins, wname.c_str(), (winid + WIN_STRING_DIV + wname + WIN_STRING_DIV + progpath).c_str()); + std::string desc = + (winid + WIN_STRING_DIV + wname + + WIN_STRING_DIV + progpath); + + obs_property_list_add_string(wins, wname.c_str(), + desc.c_str()); } - obs_properties_add_int(props, "cut_top", "Cut top pixels", 0, 4096, 1); - obs_properties_add_int(props, "cut_left", "Cut left pixels", 0, 4096, 1); - obs_properties_add_int(props, "cut_right", "Cut right pixels", 0, 4096, 1); - obs_properties_add_int(props, "cut_bot", "Cut bottom pixels", 0, 4096, 1); + obs_properties_add_int(props, "cut_top", "Cut top pixels", + 0, 4096, 1); + obs_properties_add_int(props, "cut_left", "Cut left pixels", + 0, 4096, 1); + obs_properties_add_int(props, "cut_right", "Cut right pixels", + 0, 4096, 1); + obs_properties_add_int(props, "cut_bot", "Cut bottom pixels", + 0, 4096, 1); obs_properties_add_bool(props, "swap_redblue", "Swap red and blue"); - obs_properties_add_bool(props, "lock_x", "Lock X server when capturing"); + obs_properties_add_bool(props, "lock_x", "Lock X server when " + "capturing"); return props; } @@ -89,10 +98,10 @@ struct XCompcapMain_private { XCompcapMain_private() :win(0) - ,cut_top(0),cur_cut_top(0) - ,cut_left(0),cur_cut_left(0) - ,cut_right(0),cur_cut_right(0) - ,cut_bot(0),cur_cut_bot(0) + ,cut_top(0), cur_cut_top(0) + ,cut_left(0), cur_cut_left(0) + ,cut_right(0), cur_cut_right(0) + ,cut_bot(0), cur_cut_bot(0) ,inverted(false) ,width(0),height(0) ,pixmap(0) @@ -150,8 +159,7 @@ XCompcapMain::~XCompcapMain() { ObsGsContextHolder obsctx; - if(p->tex) - { + if (p->tex) { texture_destroy(p->tex); p->tex = 0; } @@ -163,20 +171,18 @@ XCompcapMain::~XCompcapMain() static Window getWindowFromString(std::string wstr) { - if(wstr == "") - { + if (wstr == "") { return XCompcap::getTopLevelWindows().front(); } - if(wstr.substr(0, 4) == "root") - { + if (wstr.substr(0, 4) == "root") { int i = std::stoi("0" + wstr.substr(4)); return RootWindow(xdisp, i); } size_t firstMark = wstr.find(WIN_STRING_DIV); - if(firstMark == std::string::npos) + if (firstMark == std::string::npos) return (Window)std::stol(wstr); std::string widstr = wstr.substr(0, firstMark); @@ -188,14 +194,13 @@ static Window getWindowFromString(std::string wstr) std::string wname = wstr.substr(0, lastMark); Window matchedNameWin = wid; - for(Window cwin: XCompcap::getTopLevelWindows()) - { + for (Window cwin: XCompcap::getTopLevelWindows()) { std::string cwinname = XCompcap::getWindowName(cwin); - if(cwin == wid && wname == cwinname) + if (cwin == wid && wname == cwinname) return wid; - if(wname == cwinname) + if (wname == cwinname) matchedNameWin = cwin; } @@ -204,27 +209,24 @@ static Window getWindowFromString(std::string wstr) static void xcc_cleanup(XCompcapMain_private *p) { - if(p->gltex) - { + if (p->gltex) { texture_destroy(p->gltex); p->gltex = 0; } - if(p->glxpixmap) - { + if (p->glxpixmap) { glXDestroyPixmap(xdisp, p->glxpixmap); p->glxpixmap = 0; } - if(p->pixmap) - { + if (p->pixmap) { XFreePixmap(xdisp, p->pixmap); p->pixmap = 0; } - if(p->win) - { - XCompositeUnredirectWindow(xdisp, p->win, CompositeRedirectAutomatic); + if (p->win) { + XCompositeUnredirectWindow(xdisp, p->win, + CompositeRedirectAutomatic); XSelectInput(xdisp, p->win, 0); p->win = 0; } @@ -242,9 +244,11 @@ void XCompcapMain::updateSettings(obs_data_t settings) xcc_cleanup(p); - if(settings) - { - p->win = getWindowFromString(obs_data_getstring(settings, "capture_window")); + if (settings) { + const char *windowName = obs_data_getstring(settings, + "capture_window"); + + p->win = getWindowFromString(windowName); p->cut_top = obs_data_getint(settings, "cut_top"); p->cut_left = obs_data_getint(settings, "cut_left"); @@ -252,9 +256,7 @@ void XCompcapMain::updateSettings(obs_data_t settings) p->cut_bot = obs_data_getint(settings, "cut_bot"); p->lockX = obs_data_getbool(settings, "lock_x"); p->swapRedBlue = obs_data_getbool(settings, "swap_redblue"); - } - else - { + } else { p->win = prevWin; } @@ -262,9 +264,9 @@ void XCompcapMain::updateSettings(obs_data_t settings) XCompositeRedirectWindow(xdisp, p->win, CompositeRedirectAutomatic); - if(xlock.gotError()) - { - blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s", xlock.getErrorText().c_str()); + if (xlock.gotError()) { + blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s", + xlock.getErrorText().c_str()); return; } @@ -272,8 +274,7 @@ void XCompcapMain::updateSettings(obs_data_t settings) XSync(xdisp, 0); XWindowAttributes attr; - if(!XGetWindowAttributes(xdisp, p->win, &attr)) - { + if (!XGetWindowAttributes(xdisp, p->win, &attr)) { p->win = 0; p->width = 0; p->height = 0; @@ -285,35 +286,28 @@ void XCompcapMain::updateSettings(obs_data_t settings) p->width = attr.width; p->height = attr.height; - if(p->cut_top + p->cut_bot < (int)p->height) - { + if (p->cut_top + p->cut_bot < (int)p->height) { p->cur_cut_top = p->cut_top; p->cur_cut_bot = p->cut_bot; - } - else - { + } else { p->cur_cut_top = 0; p->cur_cut_bot = 0; } - if(p->cut_left + p->cut_right < (int)p->width) - { + if (p->cut_left + p->cut_right < (int)p->width) { p->cur_cut_left = p->cut_left; p->cur_cut_right = p->cut_right; - } - else - { + } else { p->cur_cut_left = 0; p->cur_cut_right = 0; } - if(p->tex) + if (p->tex) texture_destroy(p->tex); uint8_t *texData = new uint8_t[width() * height() * 4]; - for(unsigned int i = 0; i < width() * height() * 4; i += 4) - { + for (unsigned int i = 0; i < width() * height() * 4; i += 4) { texData[i + 0] = p->swapRedBlue ? 0xFF : 0; texData[i + 1] = 0; texData[i + 2] = p->swapRedBlue ? 0 : 0xFF; @@ -322,12 +316,12 @@ void XCompcapMain::updateSettings(obs_data_t settings) const uint8_t* texDataArr[] = { texData, 0 }; - p->tex = gs_create_texture(width(), height(), cf, 1, (const void**)texDataArr, 0); + p->tex = gs_create_texture(width(), height(), cf, 1, + (const void**)texDataArr, 0); delete[] texData; - if(!p->swapRedBlue) - { + if (!p->swapRedBlue) { GLuint tex = *(GLuint*)texture_getobj(p->tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); @@ -345,10 +339,11 @@ void XCompcapMain::updateSettings(obs_data_t settings) }; int nelem = 0; - GLXFBConfig* configs = glXChooseFBConfig(xdisp, XCompcap::getRootWindowScreen(attr.root), attrs, &nelem); + GLXFBConfig* configs = glXChooseFBConfig(xdisp, + XCompcap::getRootWindowScreen(attr.root), + attrs, &nelem); - if(nelem <= 0) - { + if (nelem <= 0) { blog(LOG_ERROR, "no matching fb config found"); p->win = 0; p->height = 0; @@ -363,22 +358,26 @@ void XCompcapMain::updateSettings(obs_data_t settings) p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win); - if(xlock.gotError()) - { - blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s", xlock.getErrorText().c_str()); + if (xlock.gotError()) { + blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s", + xlock.getErrorText().c_str()); p->pixmap = 0; XFree(configs); return; } - const int attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, - GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, - None }; + const int attribs[] = + { + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, + None + }; + p->glxpixmap = glXCreatePixmap(xdisp, configs[0], p->pixmap, attribs); - if(xlock.gotError()) - { - blog(LOG_ERROR, "glXCreatePixmap failed: %s", xlock.getErrorText().c_str()); + if (xlock.gotError()) { + blog(LOG_ERROR, "glXCreatePixmap failed: %s", + xlock.getErrorText().c_str()); XFreePixmap(xdisp, p->pixmap); XFree(configs); p->pixmap = 0; @@ -388,7 +387,8 @@ void XCompcapMain::updateSettings(obs_data_t settings) XFree(configs); - p->gltex = gs_create_texture(p->width, p->height, cf, 1, 0, GS_GL_DUMMYTEX); + p->gltex = gs_create_texture(p->width, p->height, cf, 1, 0, + GS_GL_DUMMYTEX); GLuint gltex = *(GLuint*)texture_getobj(p->gltex); glBindTexture(GL_TEXTURE_2D, gltex); @@ -403,31 +403,29 @@ void XCompcapMain::tick(float seconds) PLock lock(&p->lock, true); - if(!lock.isLocked()) + if (!lock.isLocked()) return; XCompcap::processEvents(); - if(XCompcap::windowWasReconfigured(p->win)) + if (XCompcap::windowWasReconfigured(p->win)) updateSettings(0); - if(!p->tex || !p->gltex) + if (!p->tex || !p->gltex) return; gs_entercontext(obs_graphics()); - if(p->lockX) - { + if (p->lockX) { XLockDisplay(xdisp); XSync(xdisp, 0); } - gs_copy_texture_region(p->tex, 0, 0, p->gltex, p->cur_cut_left, p->cur_cut_top, width(), height()); + gs_copy_texture_region(p->tex, 0, 0, p->gltex, p->cur_cut_left, + p->cur_cut_top, width(), height()); - if(p->lockX) - { + if (p->lockX) XUnlockDisplay(xdisp); - } gs_leavecontext(); } @@ -436,7 +434,7 @@ void XCompcapMain::render(effect_t effect) { PLock lock(&p->lock, true); - if(!lock.isLocked() || !p->tex) + if (!lock.isLocked() || !p->tex) return; eparam_t image = effect_getparambyname(effect, "image");