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 void qtcX11MapRaised(xcb_window_t win) { if (qtcUnlikely(!win)) return; static const uint32_t val = XCB_STACK_MODE_ABOVE; qtcX11CallVoid(configure_window, win, XCB_CONFIG_WINDOW_STACK_MODE, &val); qtcX11CallVoid(map_window, win); }
QTC_EXPORT void qtcX11InitXlib(Display *disp) { if (qtcUnlikely(qtc_xcb_conn) || !disp) { return; } qtc_disp = disp; qtcX11InitXcb(XGetXCBConnection(disp), DefaultScreen(disp)); }
QTC_EXPORT xcb_screen_t* (qtcX11GetScreen)(int screen_no) { if (screen_no == -1 || screen_no == qtc_default_screen_no) { return qtc_default_screen; } if (qtcUnlikely(!qtc_xcb_conn)) { return NULL; } return screen_of_display(qtc_xcb_conn, screen_no); }
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++; } }
QTC_EXPORT bool qtcX11IsEmbed(xcb_window_t win) { if (qtcUnlikely(!win)) return false; xcb_atom_t xembed_atom = qtc_x11_atoms[QTC_X11_ATOM_XEMBED_INFO]; xcb_get_property_reply_t *reply = qtcX11Call(get_property, 0, win, xembed_atom, xembed_atom, 0, 1); if (!reply) { return false; } bool res = xcb_get_property_value_length(reply) > 0; free(reply); return res; }
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; }
QTC_EXPORT bool qtcX11HasAlpha(xcb_window_t win) { if (qtcUnlikely(!win)) return false; if (!qtcX11CompositingActive()) { return false; } if (!win) { return true; } xcb_get_geometry_reply_t *reply = qtcX11Call(get_geometry, win); if (!reply) { return false; } bool res = (reply->depth == 32); free(reply); return res; }
QTC_EXPORT int32_t qtcX11GetShortProp(xcb_window_t win, xcb_atom_t atom) { if (qtcUnlikely(!win)) return -1; int32_t res = -1; xcb_get_property_reply_t *reply = qtcX11Call(get_property, 0, win, atom, XCB_ATOM_CARDINAL, 0, 1); if (!reply) { return -1; } if (xcb_get_property_value_length(reply) > 0) { uint32_t val = *(int32_t*)xcb_get_property_value(reply); if (val < 512) { res = val; } } free(reply); return res; }
QTC_EXPORT void qtcX11InitXcb(xcb_connection_t *conn, int screen_no) { if (qtcUnlikely(qtc_xcb_conn) || !conn) { return; } if (screen_no < 0) { screen_no = 0; } qtc_xcb_conn = conn; qtc_default_screen_no = screen_no; qtc_default_screen = screen_of_display(conn, screen_no); if (qtc_default_screen) { qtc_root_window = qtc_default_screen->root; } const size_t base_len = strlen("_NET_WM_CM_S"); sprintf(wm_cm_s_atom_name + base_len, "%d", screen_no); qtcX11GetAtoms(_QTC_X11_ATOM_NUMBER, qtc_x11_atoms, qtc_x11_atom_names, true); qtcX11ShadowInit(); }
QTC_EXPORT void qtcX11GetAtoms(size_t n, xcb_atom_t *atoms, const char *const names[], bool create) { xcb_connection_t *conn = qtc_xcb_conn; memset(atoms, 0, sizeof(xcb_atom_t) * n); if (qtcUnlikely(!conn)) { return; } xcb_intern_atom_cookie_t cookies[n]; for (size_t i = 0;i < n;i++) { cookies[i] = xcb_intern_atom(conn, !create, strlen(names[i]), names[i]); } for (size_t i = 0;i < n;i++) { xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(conn, cookies[i], 0); if (r) { atoms[i] = r->atom; free(r); } } }
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; }