QTC_EXPORT void* qtcStrLoadList(const char *str, char delim, char escape, size_t size, size_t *_nele, void *buff, size_t max_len, QtcListEleLoader loader, void *data) { QTC_RET_IF_FAIL(_nele && size && loader && str, nullptr); size_t nele = *_nele; size_t offset = 0; if (!(buff && nele)) { nele = 16; buff = malloc(16 * size); } QtCurve::StrList::forEach( str, delim, escape, [&] (const char *str, size_t len) { if (nele <= offset) { nele += 8; buff = (char*)realloc(buff, nele * size); } if (loader((char*)buff + offset * size, str, len, data)) { offset++; if (max_len && offset >= max_len) { return false; } } return true; }); *_nele = offset; if (!*_nele) { free(buff); return nullptr; } return buff; }
// WM Move QTC_EXPORT void qtcX11MoveTrigger(xcb_window_t wid, uint32_t x, uint32_t y) { QTC_RET_IF_FAIL(wid); qtcX11FlushXlib(); qtcX11CallVoid(ungrab_pointer, XCB_TIME_CURRENT_TIME); union { char _buff[32]; xcb_client_message_event_t ev; } buff; memset(&buff, 0, sizeof(buff)); // ...Taken from bespin... // stolen... errr "adapted!" from QSizeGrip // Well now it is "ported" xcb_client_message_event_t *xev = &buff.ev; xev->response_type = XCB_CLIENT_MESSAGE; xev->format = 32; xev->window = wid; xev->type = qtc_x11_net_wm_moveresize; xev->data.data32[0] = x; xev->data.data32[1] = y; xev->data.data32[2] = 8; // NET::Move xev->data.data32[3] = XCB_KEY_BUT_MASK_BUTTON_1; qtcX11SendEvent(false, qtc_root_window, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, xev); qtcX11Flush(); }
QTC_EXPORT void qtcX11ShadowUninstall(xcb_window_t win) { QTC_RET_IF_FAIL(win); qtcX11CallVoid(delete_property, win, qtc_x11_kde_net_wm_shadow); qtcX11Flush(); }
QTC_EXPORT void qtcX11ShadowInstallWithMargin(xcb_window_t win, const int margins[4]) { QTC_RET_IF_FAIL(win); if (qtcUnlikely(!margins)) { qtcX11ShadowInstall(win); return; } // In principle, I should check for _KDE_NET_WM_SHADOW in _NET_SUPPORTED. // However, it's complicated and we will gain nothing. xcb_atom_t atom = qtc_x11_kde_net_wm_shadow; if (qtc_disp) { unsigned long shadow_data[8 + 4]; memcpy(shadow_data, shadow_data_xlib, 12 * sizeof(unsigned long)); for (int i = 0;i < 4;i++) { shadow_data[i + 8] -= margins[i]; } XChangeProperty(qtc_disp, win, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)shadow_data, 12); } else { uint32_t shadow_data[8 + 4]; memcpy(shadow_data, shadow_data_xcb, 12 * sizeof(uint32_t)); for (int i = 0;i < 4;i++) { shadow_data[i + 8] -= margins[i]; } qtcX11ChangeProperty(XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 12, shadow_data); qtcX11Flush(); } }
QTC_EXPORT bool qtcPopen(const char *file, const char *const *argv, unsigned fd_num, QtcPopenFD *fds) { if (qtcUnlikely(!fds || !fd_num)) { return qtcSpawn(file, argv, NULL, NULL); } for (unsigned i = 0;i < fd_num;i++) { QTC_RET_IF_FAIL(fds[i].orig >= 0, false); } int socket_fds[2]; QTC_RET_IF_FAIL(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds) == 0, false); qtcFDSetCloexec(socket_fds[0], true); qtcFDSetCloexec(socket_fds[1], true); QtcPopenData cbdata = {socket_fds[0], fd_num, fds}; bool res = qtcSpawn(file, argv, qtcPopenCb, &cbdata, qtcPopenFailCb); if (!res) { shutdown(socket_fds[0], SHUT_RDWR); close(socket_fds[0]); shutdown(socket_fds[1], SHUT_RDWR); close(socket_fds[1]); return false; } close(socket_fds[0]); for (unsigned i = 0;i < fd_num;i++) { if ((fds[i].replace = qtcRecvFD(socket_fds[1])) < 0) { res = false; for (unsigned j = 0;j < i;j++) { if (fds[i].replace) { shutdown(fds[i].replace, SHUT_RDWR); close(fds[i].replace); } } break; } if (!(fds[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) { close(fds[i].replace); fds[i].replace = -1; continue; } } shutdown(socket_fds[1], SHUT_RDWR); close(socket_fds[1]); return res; }
static bool qtcSignalHandlerSet(int sig) { struct sigaction oact; QTC_RET_IF_FAIL(sigaction(sig, NULL, &oact) == 0, false); void *handler = ((oact.sa_flags & SA_SIGINFO) ? (void*)oact.sa_handler : (void*)oact.sa_sigaction); return qtcNoneOf(handler, SIG_DFL, SIG_IGN); }
// Necessary? void qtcX11ShadowDestroy() { QTC_RET_IF_FAIL(qtc_xcb_conn); for (unsigned int i = 0; i < sizeof(shadow_xpixmaps) / sizeof(shadow_xpixmaps[0]);i++) { qtcX11CallVoid(free_pixmap, shadow_xpixmaps[i]); } qtcX11Flush(); }
void ShadowHelper::uninstallX11Shadows(QWidget *widget) const { // DO NOT condition compile on QTC_ENABLE_X11. // There's no direct linkage on X11 and the following code will just do // nothing if X11 is not enabled (either at compile time or at run time). QTC_RET_IF_FAIL(qtcX11Enabled()); if (WId wid = qtcGetWid(widget)) { qtcX11ShadowUninstall(wid); } }
QTC_EXPORT void qtcX11ShadowInstall(xcb_window_t win) { QTC_RET_IF_FAIL(win); // In principle, I should check for _KDE_NET_WM_SHADOW in _NET_SUPPORTED. // However, it's complicated and we will gain nothing. xcb_atom_t atom = qtc_x11_kde_net_wm_shadow; if (qtc_disp) { XChangeProperty(qtc_disp, win, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)shadow_data_xlib, 12); } else { qtcX11ChangeProperty(XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 12, shadow_data_xcb); qtcX11Flush(); } }
void setup(GtkWidget *widget) { QTC_RET_IF_FAIL(widget); GtkWidget *parent = nullptr; if (GTK_IS_WINDOW(widget) && !gtk_window_get_decorated(GTK_WINDOW(widget))) { return; } if (GTK_IS_EVENT_BOX(widget) && gtk_event_box_get_above_child(GTK_EVENT_BOX(widget))) return; parent = gtk_widget_get_parent(widget); // widgets used in tabs also must be ignored (happens, unfortunately) if (GTK_IS_NOTEBOOK(parent) && Tab::isLabel(GTK_NOTEBOOK(parent), widget)) return; /* check event mask (for now we only need to do that for GtkWindow) The idea is that if the window has been set to receive button_press and button_release events (which is not done by default), it likely means that it does something with such events, in which case we should not use them for grabbing */ if (oneOf(gTypeName(widget), "GtkWindow") && (gtk_widget_get_events(widget) & (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK))) return; GtkWidgetProps props(widget); if (!isFakeGtk() && !props->wmMoveHacked) { props->wmMoveHacked = true; gtk_widget_add_events(widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON1_MOTION_MASK); registerBtnReleaseHook(); props->wmMoveDestroy.conn("destroy-event", destroy); props->wmMoveStyleSet.conn("style-set", styleSet); props->wmMoveMotion.conn("motion-notify-event", motion); props->wmMoveLeave.conn("leave-notify-event", leave); props->wmMoveButtonPress.conn("button-press-event", buttonPress); } }
QTC_EXPORT bool qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb) { QTC_RET_IF_FAIL(cb, false); // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is // a signal handler registered for SIGCHLD and the child process exit // inside waitpid()/wait(), it will be run after the process state is // cleared and would therefore block if it call wait() (or waitpid(-1)) // and if there are other child processes. As a workaround we only call // waitpid() if the main program did not set up any signal handlers for // SIGCHLD. See (the RATIONALE section of) wait(3P) for more detail. pid_t child = fork(); if (child < 0) { return false; } else if (child == 0) { pid_t grandchild = fork(); if (grandchild < 0) { qtcCall(fail_cb, data); _exit(1); } else if (grandchild == 0) { /* grandchild */ cb(data); _exit(0); } else { _exit(0); } return true; } else { /* parent */ if (qtcSignalHandlerSet(SIGCHLD)) { // If we create a child process, the signal handler will recieve // the signal anyway (and there is no way to only block SIGCHLD // only for our child process). Since the signal handler may // hang and should already take care of getting rid of // zombie processes, we do not call waitpid in this case.... return true; } // If SIGCHLD is ignored, waitpid will return -1 with errno // set to ECHILD, treat this as success (good enough for our purpose // and not likely to fail anyway...) int status = 0; return ((waitpid(child, &status, 0) > 0 && status == 0) || errno == ECHILD); } }
// Blur QTC_EXPORT void qtcX11BlurTrigger(xcb_window_t wid, bool enable, unsigned prop_num, const uint32_t *props) { QTC_RET_IF_FAIL(wid); xcb_atom_t atom = qtc_x11_kde_net_wm_blur_behind_region; if (enable) { if (qtc_disp) { QTC_DEF_LOCAL_BUFF(unsigned long, xlib_props, 256, prop_num); for (unsigned i = 0;i < prop_num;i++) { xlib_props.p[i] = props[i]; } XChangeProperty(qtc_disp, wid, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)xlib_props.p, prop_num); QTC_FREE_LOCAL_BUFF(xlib_props); } else { qtcX11ChangeProperty(XCB_PROP_MODE_REPLACE, wid, atom, XCB_ATOM_CARDINAL, 32, prop_num, props); } } else {
QTC_EXPORT bool qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb) { QTC_RET_IF_FAIL(cb, false); // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is // a signal handler registered for SIGCHLD and the child process exit // inside waitpid()/wait(), it will be run after the process state is // cleared and would therefore block if it call wait() (or waitpid(-1)) // and if there are other child processes. As a workaround we use vfork() // to block the parent until direct child exit so that waitpid() will always // be called after the process exit and would never hang because of signal // handler. See (the RATIONALE section of) wait(3P) for more detail. pid_t child = vfork(); if (child < 0) { return false; } else if (child == 0) { pid_t grandchild = fork(); if (grandchild < 0) { qtcCall(fail_cb, data); _exit(1); } else if (grandchild == 0) { /* grandchild */ cb(data); _exit(0); } else { _exit(0); } return true; } else { /* parent */ // If SIGCHLD is ignored, waitpid will return -1 with errno // set to ECHILD, treat this as success (good enough for our purpose // and not likely to fail anyway...) int status = 0; return ((waitpid(child, &status, 0) > 0 && status == 0) || errno == ECHILD); } }
QTC_EXPORT void _forEach(const char *str, char delim, char escape, const std::function<bool(const char*, size_t)> &func) { QTC_RET_IF_FAIL(str); Str::Buff<1024> buff; if (qtcUnlikely(escape == delim)) { escape = '\0'; } const char key[] = {delim, escape, '\0'}; const char *p = str; while (true) { size_t len = 0; while (true) { size_t sub_len = strcspn(p, key); buff.resize(len + sub_len + 2); memcpy(buff.get() + len, p, sub_len); len += sub_len; p += sub_len; if (escape && *p == escape) { buff[len] = p[1]; if (qtcUnlikely(!p[1])) { p++; break; } len++; p += 2; } else { buff[len] = '\0'; break; } } if (!func(buff.get(), len) || !*p) { break; } p++; } }
bool ShadowHelper::installX11Shadows(QWidget *widget) { // DO NOT condition compile on QTC_ENABLE_X11. // There's no direct linkage on X11 and the following code will just do // nothing if X11 is not enabled (either at compile time or at run time). QTC_RET_IF_FAIL(qtcX11Enabled(), false); if (WId wid = qtcGetWid(widget)) { if (widget->windowType() == Qt::ToolTip && widget->inherits("QBalloonTip")) { bool atTop = true; int margin = qtcGetBalloonMargin(widget, &atTop); int margins[4] = {0, 0, 0, 0}; // KWin's shadows margin order is top, right, bottom, left.. margins[atTop ? 0 : 2] = margin; qtcX11ShadowInstall(wid, margins); } else { qtcX11ShadowInstall(wid); } return true; } return false; }
QTC_EXPORT bool qtcPopenBuff(const char *file, const char *const argv[], unsigned buff_num, QtcPopenBuff *buffs, int timeout) { if (qtcUnlikely(!buffs || !buff_num)) { return qtcSpawn(file, argv, NULL, NULL); } bool need_poll = false; for (unsigned i = 0;i < buff_num;i++) { QTC_RET_IF_FAIL(buffs[i].orig >= 0, false); QTC_RET_IF_FAIL(!(buffs[i].mode & QTC_POPEN_READ && buffs[i].mode & QTC_POPEN_WRITE), false); if (buffs[i].mode & QTC_POPEN_READ || buffs[i].mode & QTC_POPEN_WRITE) { need_poll = true; } } QTC_DEF_LOCAL_BUFF(QtcPopenFD, fds, 16, buff_num); for (unsigned i = 0;i < buff_num;i++) { fds.p[i].orig = buffs[i].orig; fds.p[i].replace = -1; fds.p[i].mode = buffs[i].mode; } bool res = qtcPopen(file, argv, buff_num, fds.p); if (!res) { QTC_FREE_LOCAL_BUFF(fds); return false; } for (unsigned i = 0;i < buff_num;i++) { buffs[i].orig = fds.p[i].replace; if (fds.p[i].replace >= 0) { qtcFDSetNonBlock(fds.p[i].replace, true); qtcFDSetCloexec(fds.p[i].replace, true); } } QTC_FREE_LOCAL_BUFF(fds); if (!need_poll) { return true; } QTC_DEF_LOCAL_BUFF(struct pollfd, poll_fds, 16, buff_num); QTC_DEF_LOCAL_BUFF(int, indexes, 16, buff_num); unsigned poll_fd_num = 0; for (unsigned i = 0;i < buff_num;i++) { if (!(buffs[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) { close(buffs[i].orig); continue; } indexes.p[poll_fd_num] = i; struct pollfd *cur_fd = &poll_fds.p[poll_fd_num]; cur_fd->fd = buffs[i].orig; cur_fd->events = (buffs[i].mode & QTC_POPEN_READ) ? POLLIN : POLLOUT; poll_fd_num++; } uint64_t start_time = qtcGetTime(); int poll_timeout = timeout; while (true) { int ret = poll(poll_fds.p, poll_fd_num, poll_timeout); if (ret == -1) { if (errno == EINTR) { if (!qtcPopenPollCheckTimeout(start_time, timeout, &poll_timeout)) { break; } continue; } break; } else if (ret == 0) { break; } for (unsigned i = 0;i < poll_fd_num;i++) { struct pollfd *cur_fd = &poll_fds.p[i]; if (cur_fd->revents & POLLIN) { if (!qtcPopenReadBuff(&buffs[indexes.p[i]])) { cur_fd->events &= ~POLLIN; } } else if (cur_fd->revents & POLLOUT) { if (!qtcPopenWriteBuff(&buffs[indexes.p[i]])) { cur_fd->events &= ~POLLOUT; } } if (cur_fd->revents & (POLLERR | POLLHUP | POLLNVAL) || !(cur_fd->events & (POLLIN | POLLOUT))) { shutdown(cur_fd->fd, SHUT_RDWR); close(cur_fd->fd); poll_fd_num--; memmove(cur_fd, cur_fd + 1, (poll_fd_num - i) * sizeof(struct pollfd)); memmove(indexes.p + i, indexes.p + i + 1, (poll_fd_num - i) * sizeof(int)); i--; } } if (poll_fd_num <= 0 || !qtcPopenPollCheckTimeout(start_time, timeout, &poll_timeout)) { break; } } for (unsigned i = 0;i < poll_fd_num;i++) { struct pollfd *cur_fd = &poll_fds.p[i]; shutdown(cur_fd->fd, SHUT_RDWR); close(cur_fd->fd); } QTC_FREE_LOCAL_BUFF(indexes); QTC_FREE_LOCAL_BUFF(poll_fds); return true; }