client.c 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  1. /*
  2. * client.c
  3. * Copyright © 2008, 2009 Martin Duquesnoy <xorg62@gmail.com>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are
  8. * met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above
  13. * copyright notice, this list of conditions and the following disclaimer
  14. * in the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of the nor the names of its
  17. * contributors may be used to endorse or promote products derived from
  18. * this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. #include "wmfs.h"
  33. /** Attach client in the client chain
  34. * \param c Client pointer
  35. */
  36. void
  37. client_attach(Client *c)
  38. {
  39. if(clients)
  40. clients->prev = c;
  41. c->next = clients;
  42. clients = c;
  43. return;
  44. }
  45. /** Send a ConfigureRequest event to the Client
  46. * \param c Client pointer
  47. */
  48. void
  49. client_configure(Client *c)
  50. {
  51. XConfigureEvent ev;
  52. ev.type = ConfigureNotify;
  53. ev.event = c->win;
  54. ev.window = c->win;
  55. ev.x = c->geo.x;
  56. ev.y = c->geo.y;
  57. ev.width = c->geo.width;
  58. ev.height = c->geo.height;
  59. ev.above = None;
  60. ev.border_width = 0;
  61. ev.override_redirect = 0;
  62. XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ev);
  63. return;
  64. }
  65. /** Detach a client to the client chain
  66. * \param c Client pointer
  67. */
  68. void
  69. client_detach(Client *c)
  70. {
  71. Client **cc;
  72. for(cc = &clients; *cc && *cc != c; cc = &(*cc)->next);
  73. *cc = c->next;
  74. return;
  75. }
  76. /** Get the next client
  77. *\return The next client or NULL
  78. */
  79. Client*
  80. client_get_next(void)
  81. {
  82. Client *c = NULL;
  83. if(!sel || ishide(sel, selscreen))
  84. return NULL;
  85. for(c = sel->next; c && ishide(c, selscreen); c = c->next);
  86. if(!c)
  87. for(c = clients; c && ishide(c, selscreen); c = c->next);
  88. return c;
  89. }
  90. /** Get the previous client
  91. *\return The previous client or NULL
  92. */
  93. Client*
  94. client_get_prev(void)
  95. {
  96. Client *c = NULL, *d;
  97. if(!sel || ishide(sel, selscreen))
  98. return NULL;
  99. for(d = clients; d != sel; d = d->next)
  100. if(!ishide(d, selscreen))
  101. c = d;
  102. if(!c)
  103. for(; d; d = d->next)
  104. if(!ishide(d, selscreen))
  105. c = d;
  106. return c;
  107. }
  108. /** Switch to the previous client
  109. * \param cmd uicb_t type unused
  110. */
  111. void
  112. uicb_client_prev(uicb_t cmd)
  113. {
  114. Client *c;
  115. if((c = client_get_prev()))
  116. {
  117. client_focus(c);
  118. client_raise(c);
  119. }
  120. return;
  121. }
  122. /** Switch to the next client
  123. * \param cmd uicb_t type unused
  124. */
  125. void
  126. uicb_client_next(uicb_t cmd)
  127. {
  128. Client *c;
  129. if((c = client_get_next()))
  130. {
  131. client_focus(c);
  132. client_raise(c);
  133. }
  134. return;
  135. }
  136. /** Swap the current client with the next one
  137. *\param cmd uicb_t type unused
  138. */
  139. void
  140. uicb_client_swap_next(uicb_t cmd)
  141. {
  142. Client *c;
  143. if((c = client_get_next()))
  144. {
  145. client_swap(sel, c);
  146. client_focus(c);
  147. }
  148. return;
  149. }
  150. /** Swap the current client with the previous one
  151. *\param cmd uicb_t type unused
  152. */
  153. void
  154. uicb_client_swap_prev(uicb_t cmd)
  155. {
  156. Client *c;
  157. if((c = client_get_prev()))
  158. {
  159. client_swap(sel, c);
  160. client_focus(c);
  161. }
  162. return;
  163. }
  164. /** Set the client c above
  165. *\param c Client pointer
  166. */
  167. void
  168. client_above(Client *c)
  169. {
  170. XRectangle geo = { 0 };
  171. if(c->flags & AboveFlag)
  172. return;
  173. c->flags |= AboveFlag;
  174. geo.height = spgeo[c->screen].height * 0.75;
  175. geo.width = spgeo[c->screen].width * 0.75;
  176. geo.y = spgeo[c->screen].y + (spgeo[c->screen].height / 2) - (geo.height / 2);
  177. geo.x = spgeo[c->screen].x + (spgeo[c->screen].width / 2)- (geo.width / 2);
  178. client_moveresize(c, geo, tags[c->screen][c->tag].resizehint);
  179. client_raise(c);
  180. tags[c->screen][c->tag].layout.func(c->screen);
  181. return;
  182. }
  183. /** Set the focus to a client
  184. * \param c Client pointer
  185. */
  186. void
  187. client_focus(Client *c)
  188. {
  189. Window w;
  190. int d;
  191. if(sel && sel != c)
  192. {
  193. sel->colors.frame = conf.client.bordernormal;
  194. sel->colors.fg = conf.titlebar.fg_normal;
  195. sel->colors.resizecorner = conf.client.resizecorner_normal;
  196. if(TBARH - BORDH && sel->titlebar->stipple)
  197. sel->titlebar->stipple_color = conf.titlebar.stipple.colors.normal;
  198. if(sel->flags & AboveFlag)
  199. sel->flags &= ~AboveFlag;
  200. frame_update(sel);
  201. mouse_grabbuttons(sel, False);
  202. }
  203. if((sel = c))
  204. {
  205. c->colors.frame = conf.client.borderfocus;
  206. c->colors.fg = conf.titlebar.fg_focus;
  207. c->colors.resizecorner = conf.client.resizecorner_focus;
  208. if(TBARH - BORDH && c->titlebar->stipple)
  209. c->titlebar->stipple_color = conf.titlebar.stipple.colors.focus;
  210. frame_update(c);
  211. mouse_grabbuttons(c, True);
  212. if(conf.raisefocus)
  213. {
  214. XQueryPointer(dpy, ROOT, &w, &w, &d, &d, &d, &d, (uint *)&d);
  215. if(c == client_gb_win(w)
  216. || c == client_gb_frame(w)
  217. || c == client_gb_titlebar(w))
  218. client_raise(c);
  219. }
  220. if(tags[sel->screen][sel->tag].abovefc
  221. && !conf.focus_fmouse)
  222. client_above(sel);
  223. XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
  224. }
  225. else
  226. XSetInputFocus(dpy, ROOT, RevertToPointerRoot, CurrentTime);
  227. return;
  228. }
  229. /* The following function have the same point :
  230. * find a client member with a Window {{{
  231. */
  232. /* Get Client with a window */
  233. /** Get a client->win with a window
  234. * \param w Window
  235. * \return The client
  236. */
  237. Client* client_gb_win(Window w)
  238. {
  239. Client *c;
  240. for(c = clients; c && c->win != w; c = c->next);
  241. return c;
  242. }
  243. /** Get a client->frame with a window
  244. * \param w Window
  245. * \return The client
  246. */
  247. Client* client_gb_frame(Window w)
  248. {
  249. Client *c;
  250. for(c = clients; c && c->frame != w; c = c->next);
  251. return c;
  252. }
  253. /** Get a client->titlebar with a window
  254. * \param w Window
  255. * \return The client
  256. */
  257. Client* client_gb_titlebar(Window w)
  258. {
  259. Client *c;
  260. if(!(TBARH - BORDH))
  261. return NULL;
  262. for(c = clients; c && c->titlebar->win != w; c = c->next);
  263. return c;
  264. }
  265. /** Get a client->resize with a window
  266. * \param w Window
  267. * \return The client
  268. */
  269. Client* client_gb_resize(Window w)
  270. {
  271. Client *c;
  272. for(c = clients; (c && c->resize[Right] != w) && (c && c->resize[Left] != w); c = c->next);
  273. return c;
  274. }
  275. /** Get a client->button[button_num] with a window
  276. * \param w Window
  277. * \param n Pointer who return the button_num
  278. * \return The client
  279. */
  280. Client* client_gb_button(Window w, int *n)
  281. {
  282. Client *c;
  283. int i;
  284. if(!BUTTONWH || !(TBARH - BORDH))
  285. return NULL;
  286. for(c = clients; c; c = c->next)
  287. for(i = 0; i < conf.titlebar.nbutton; ++i)
  288. if(c->button[i] == w)
  289. {
  290. *n = i;
  291. return c;
  292. }
  293. return NULL;
  294. }
  295. /* }}} */
  296. /** Get a client name
  297. * \param c Client pointer
  298. */
  299. void
  300. client_get_name(Client *c)
  301. {
  302. Atom rt;
  303. int rf;
  304. ulong ir, il;
  305. /* This one instead XFetchName for utf8 name support */
  306. if(XGetWindowProperty(dpy, c->win, net_atom[net_wm_name], 0, 4096,
  307. False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title) != Success)
  308. XGetWindowProperty(dpy, c->win, ATOM("WM_NAME"), 0, 4096,
  309. False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title);
  310. /* If there is no title... */
  311. if(!c->title)
  312. {
  313. XFetchName(dpy, c->win, &(c->title));
  314. if(!c->title)
  315. c->title = _strdup("WMFS");
  316. }
  317. frame_update(c);
  318. return;
  319. }
  320. /** Hide a client (for tag switching)
  321. * \param c Client pointer
  322. */
  323. void
  324. client_hide(Client *c)
  325. {
  326. CHECK(!(c->flags & HideFlag));
  327. client_unmap(c);
  328. c->flags |= HideFlag;
  329. setwinstate(c->win, IconicState);
  330. return;
  331. }
  332. /** Check if the client 'c' is hide
  333. * \param c Client pointer
  334. * \return True if the client is hide; False if not
  335. */
  336. Bool
  337. ishide(Client *c, int screen)
  338. {
  339. if(c->tag == seltag[screen] && c->screen == screen)
  340. return False;
  341. return True;
  342. }
  343. /** Kill a client
  344. * \param c Client pointer
  345. */
  346. void
  347. client_kill(Client *c)
  348. {
  349. XEvent ev;
  350. Atom *atom = NULL;
  351. int proto;
  352. int canbedel = 0;
  353. CHECK(c);
  354. if(XGetWMProtocols(dpy, c->win, &atom, &proto) && atom)
  355. {
  356. while(proto--)
  357. if(atom[proto] == ATOM("WM_DELETE_WINDOW"))
  358. ++canbedel;
  359. XFree(atom);
  360. if(canbedel)
  361. {
  362. ev.type = ClientMessage;
  363. ev.xclient.window = c->win;
  364. ev.xclient.message_type = ATOM("WM_PROTOCOLS");
  365. ev.xclient.format = 32;
  366. ev.xclient.data.l[0] = ATOM("WM_DELETE_WINDOW");
  367. ev.xclient.data.l[1] = CurrentTime;
  368. ev.xclient.data.l[2] = 0;
  369. ev.xclient.data.l[3] = 0;
  370. ev.xclient.data.l[4] = 0;
  371. XSendEvent(dpy, c->win, False, NoEventMask, &ev);
  372. }
  373. else
  374. XKillClient(dpy, c->win);
  375. }
  376. else
  377. XKillClient(dpy, c->win);
  378. return;
  379. }
  380. /** Kill the selected client
  381. * \param cmd uicb_t type unused
  382. */
  383. void
  384. uicb_client_kill(uicb_t cmd)
  385. {
  386. CHECK(sel);
  387. client_kill(sel);
  388. return;
  389. }
  390. /** Map a client
  391. * \param c Client pointer
  392. */
  393. void
  394. client_map(Client *c)
  395. {
  396. CHECK(c);
  397. if(c->flags & FSSFlag)
  398. XMapWindow(dpy, c->win);
  399. else
  400. {
  401. XMapWindow(dpy, c->frame);
  402. XMapSubwindows(dpy, c->frame);
  403. if(TBARH - BORDH)
  404. {
  405. barwin_map(c->titlebar);
  406. barwin_map_subwin(c->titlebar);
  407. }
  408. XMapSubwindows(dpy, c->frame);
  409. c->flags &= ~UnmapFlag;
  410. }
  411. return;
  412. }
  413. /** Manage a client with a window and his attributes
  414. * \param w Cient's futur Window
  415. * \param wa XWindowAttributes pointer, Window w attributes
  416. * \param ar Do arrange() or not (Bool)
  417. * \return The managed client
  418. */
  419. Client*
  420. client_manage(Window w, XWindowAttributes *wa, Bool ar)
  421. {
  422. Client *c, *t = NULL;
  423. Window trans, dw;
  424. Status rettrans;
  425. XSetWindowAttributes at;
  426. int mx, my, dint;
  427. uint duint;
  428. screen_get_sel();
  429. c = emalloc(1, sizeof(Client));
  430. c->win = w;
  431. c->screen = selscreen;
  432. c->flags = 0;
  433. XQueryPointer(dpy, ROOT, &dw, &dw, &mx, &my, &dint, &dint, &duint);
  434. if(conf.client.place_at_mouse)
  435. {
  436. mx += BORDH;
  437. my += TBARH;
  438. if((MAXW - mx) < wa->width)
  439. mx -= wa->width + BORDH;
  440. if((MAXH - my) < wa->height)
  441. my -= wa->height + TBARH;
  442. }
  443. else
  444. {
  445. mx = wa->x + BORDH;
  446. my = wa->y + TBARH + INFOBARH;
  447. /* Check if the client is already in the selected
  448. * screen, else place the client in it */
  449. if(screen_get_with_geo(mx, my) != selscreen)
  450. {
  451. mx += spgeo[selscreen].x;
  452. my += spgeo[selscreen].y;
  453. }
  454. }
  455. c->ogeo.x = c->geo.x = mx;
  456. c->ogeo.y = c->geo.y = my;
  457. c->ogeo.width = c->geo.width = wa->width;
  458. c->ogeo.height = c->geo.height = wa->height;
  459. c->tag = seltag[c->screen];
  460. c->layer = (sel && sel->layer > 0) ? sel->layer : 1;
  461. at.event_mask = PropertyChangeMask;
  462. frame_create(c);
  463. client_size_hints(c);
  464. XChangeWindowAttributes(dpy, c->win, CWEventMask, &at);
  465. XSetWindowBorderWidth(dpy, c->win, 0); /* client win sould _not_ have borders */
  466. mouse_grabbuttons(c, False);
  467. if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
  468. for(t = clients; t && t->win != trans; t = t->next);
  469. if(t)
  470. c->tag = t->tag;
  471. if(!(c->flags & FreeFlag)
  472. && (rettrans == Success || (c->flags & HintFlag)))
  473. c->flags |= FreeFlag;
  474. free(t);
  475. client_attach(c);
  476. client_get_name(c);
  477. client_map(c);
  478. client_raise(c);
  479. client_focus(c);
  480. setwinstate(c->win, NormalState);
  481. ewmh_get_client_list();
  482. ewmh_manage_window_type(c);
  483. client_set_wanted_tag(c);
  484. client_update_attributes(c);
  485. if(ar)
  486. arrange(c->screen, True);
  487. if(!conf.client.set_new_win_master)
  488. layout_set_client_master(c);
  489. return c;
  490. }
  491. /** Set a geometry with the client size hints
  492. *\param geo Geometry pointer
  493. *\param c Client pointer
  494. */
  495. void
  496. client_geo_hints(XRectangle *geo, Client *c)
  497. {
  498. /* minimum possible */
  499. if(geo->width < 1)
  500. geo->width = 1;
  501. if(geo->height < 1)
  502. geo->height = 1;
  503. /* base */
  504. geo->width -= c->basew;
  505. geo->height -= c->baseh;
  506. /* aspect */
  507. if(c->minay > 0 && c->maxay > 0
  508. && c->minax > 0 && c->maxax > 0)
  509. {
  510. if(geo->width * c->maxay > geo->height * c->maxax)
  511. geo->width = geo->height * c->maxax / c->maxay;
  512. else if(geo->width * c->minay < geo->height * c->minax)
  513. geo->height = geo->width * c->minay / c->minax;
  514. }
  515. /* incremental */
  516. if(c->incw)
  517. geo->width -= geo->width % c->incw;
  518. if(c->inch)
  519. geo->height -= geo->height % c->inch;
  520. /* base dimension */
  521. geo->width += c->basew;
  522. geo->height += c->baseh;
  523. if(c->minw > 0 && geo->width < c->minw)
  524. geo->width = c->minw;
  525. if(c->minh > 0 && geo->height < c->minh)
  526. geo->height = c->minh;
  527. if(c->maxw > 0 && geo->width > c->maxw)
  528. geo->width = c->maxw;
  529. if(c->maxh > 0 && geo->height > c->maxh)
  530. geo->height = c->maxh;
  531. return;
  532. }
  533. /** Move and Resize a client
  534. * \param c Client pointer
  535. * \param geo Coordinate info for the future size
  536. * of the client
  537. * \param r Bool for resize hint or not
  538. */
  539. void
  540. client_moveresize(Client *c, XRectangle geo, Bool r)
  541. {
  542. int os;
  543. if(!c)
  544. return;
  545. os = c->screen;
  546. if(r)
  547. client_geo_hints(&geo, c);
  548. c->flags &= ~MaxFlag;
  549. c->geo = c->ogeo = geo;
  550. if((c->screen = screen_get_with_geo(c->geo.x, c->geo.y)) != os)
  551. c->tag = seltag[c->screen];
  552. frame_moveresize(c, c->geo);
  553. XMoveResizeWindow(dpy, c->win, BORDH, TBARH, c->geo.width, c->geo.height);
  554. client_configure(c);
  555. client_update_attributes(c);
  556. return;
  557. }
  558. /** Maximize a client
  559. * \param c Client pointer
  560. */
  561. void
  562. client_maximize(Client *c)
  563. {
  564. XRectangle geo;
  565. if(!c || c->flags & FSSFlag)
  566. return;
  567. c->screen = screen_get_with_geo(c->geo.x, c->geo.y);
  568. geo.x = sgeo[c->screen].x;
  569. geo.y = sgeo[c->screen].y;
  570. geo.width = sgeo[c->screen].width - BORDH * 2;
  571. geo.height = sgeo[c->screen].height - BORDH;
  572. client_moveresize(c, geo, False);
  573. return;
  574. }
  575. /** Get client size hints
  576. * \param c Client pointer
  577. */
  578. void
  579. client_size_hints(Client *c)
  580. {
  581. long msize;
  582. XSizeHints size;
  583. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  584. size.flags = PSize;
  585. /* base */
  586. if(size.flags & PBaseSize)
  587. {
  588. c->basew = size.base_width;
  589. c->baseh = size.base_height;
  590. }
  591. else if(size.flags & PMinSize)
  592. {
  593. c->basew = size.min_width;
  594. c->baseh = size.min_height;
  595. }
  596. else
  597. c->basew = c->baseh = 0;
  598. /* inc */
  599. if(size.flags & PResizeInc)
  600. {
  601. c->incw = size.width_inc;
  602. c->inch = size.height_inc;
  603. }
  604. else
  605. c->incw = c->inch = 0;
  606. /* max */
  607. if(size.flags & PMaxSize)
  608. {
  609. c->maxw = size.max_width;
  610. c->maxh = size.max_height;
  611. }
  612. else
  613. c->maxw = c->maxh = 0;
  614. /* min */
  615. if(size.flags & PMinSize)
  616. {
  617. c->minw = (size.min_width ? size.min_width : 1);
  618. c->minh = (size.min_height ? size.min_height: 1);
  619. }
  620. else if(size.flags & PBaseSize)
  621. {
  622. c->minw = (size.base_width ? size.base_width : 1);
  623. c->minh = (size.base_height ? size.base_height : 1);
  624. }
  625. else
  626. c->minw = c->minh = 0;
  627. /* aspect */
  628. if(size.flags & PAspect)
  629. {
  630. c->minax = size.min_aspect.x;
  631. c->maxax = size.max_aspect.x;
  632. c->minay = size.min_aspect.y;
  633. c->maxay = size.max_aspect.y;
  634. }
  635. else
  636. c->minax = c->maxax = c->minay = c->maxay = 0;
  637. if(c->maxw && c->minw && c->maxh && c->minh
  638. && c->maxw == c->minw && c->maxh == c->minh)
  639. c->flags |= HintFlag;
  640. return;
  641. }
  642. /** Swap two clients
  643. *\param 1 c1 First client
  644. *\param 2 c2 Second client
  645. */
  646. void
  647. client_swap(Client *c1, Client *c2)
  648. {
  649. /* Check if no one of these clients are free */
  650. CHECK(!(c1->flags & FreeFlag));
  651. CHECK(!(c2->flags & FreeFlag));
  652. if(c1 == c2 || (c1->screen == c2->screen && c1->tag != c2->tag))
  653. return;
  654. /* Swap only the windows */
  655. swap_ptr((void**)&c1->win, (void**)&c2->win);
  656. /* Re-adapt the windows position with its new frame */
  657. XReparentWindow(dpy, c1->win, c1->frame, BORDH, TBARH);
  658. XReparentWindow(dpy, c2->win, c2->frame, BORDH, TBARH);
  659. /* Re-set size hints properties */
  660. client_size_hints(c1);
  661. client_size_hints(c2);
  662. /* Resize the windows */
  663. client_moveresize(c1, c1->geo, False);
  664. client_moveresize(c2, c2->geo, False);
  665. /* Get the new client name */
  666. client_get_name(c1);
  667. client_get_name(c2);
  668. return;
  669. }
  670. /** Set the wanted tag of a client
  671. *\param c Client pointer
  672. */
  673. void
  674. client_set_wanted_tag(Client *c)
  675. {
  676. XClassHint xch = { 0 };
  677. int i, j, k;
  678. XGetClassHint(dpy, c->win, &xch);
  679. for(i = 0; i < screen_count(); ++i)
  680. for(j = 1; j < conf.ntag[i] + 1; ++j)
  681. if(tags[i][j].clients)
  682. for(k = 0; k < tags[i][j].nclients; ++k)
  683. if((xch.res_name && strstr(xch.res_name, tags[i][j].clients[k]))
  684. || (xch.res_class && strstr(xch.res_class, tags[i][j].clients[k])))
  685. {
  686. c->screen = i;
  687. c->tag = j;
  688. if(c->tag != seltag[selscreen])
  689. tags[c->screen][c->tag].request_update = True;
  690. tags[c->screen][c->tag].layout.func(c->screen);
  691. }
  692. return;
  693. }
  694. /** Update client attributes (_WMFS_TAG _WMFS_SCREEN)
  695. *\param c Client pointer
  696. */
  697. void
  698. client_update_attributes(Client *c)
  699. {
  700. Bool f;
  701. /* For reload use */
  702. XChangeProperty(dpy, c->win, ATOM("_WMFS_TAG"), XA_CARDINAL, 32,
  703. PropModeReplace, (uchar*)&(c->tag), 1);
  704. XChangeProperty(dpy, c->win, ATOM("_WMFS_SCREEN"), XA_CARDINAL, 32,
  705. PropModeReplace, (uchar*)&(c->screen), 1);
  706. f = (c->flags & FreeFlag) ? True : False;
  707. XChangeProperty(dpy, c->win, ATOM("_WMFS_ISFREE"), XA_CARDINAL, 32,
  708. PropModeReplace, (uchar*)&f, 1);
  709. return;
  710. }
  711. /** Raise a client
  712. * \param c Client pointer
  713. */
  714. void
  715. client_raise(Client *c)
  716. {
  717. if(!c || ((c->flags & TileFlag) && !(c->flags & AboveFlag)))
  718. return;
  719. XRaiseWindow(dpy, c->frame);
  720. return;
  721. }
  722. /** Raise the selected client
  723. * \param cmd uicb_t type unused
  724. */
  725. void
  726. uicb_client_raise(uicb_t cmd)
  727. {
  728. client_raise(sel);
  729. return;
  730. }
  731. /** UnHide a client (for tag switching)
  732. * \param c Client pointer
  733. */
  734. void
  735. client_unhide(Client *c)
  736. {
  737. CHECK(c->flags & HideFlag);
  738. client_map(c);
  739. c->flags &= ~HideFlag;
  740. setwinstate(c->win, NormalState);
  741. return;
  742. }
  743. /** Unmanage a client
  744. * \param c Client pointer
  745. */
  746. void
  747. client_unmanage(Client *c)
  748. {
  749. Client *c_next = NULL;
  750. Bool b = False;
  751. int i;
  752. XGrabServer(dpy);
  753. XSetErrorHandler(errorhandlerdummy);
  754. XReparentWindow(dpy, c->win, ROOT, c->geo.x, c->geo.y);
  755. if(sel == c)
  756. client_focus(NULL);
  757. client_detach(c);
  758. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  759. setwinstate(c->win, WithdrawnState);
  760. frame_delete(c);
  761. XSync(dpy, False);
  762. XUngrabServer(dpy);
  763. ewmh_get_client_list();
  764. /* Arrange */
  765. for(i = 0; i < screen_count() && !b; ++i)
  766. if(c->tag == seltag[i])
  767. b = True;
  768. if(b)
  769. tags[c->screen][c->tag].layout.func(c->screen);
  770. else
  771. {
  772. tags[c->screen][c->tag].request_update = True;
  773. infobar_draw(c->screen);
  774. }
  775. XFree(c->title);
  776. /* To focus the previous client */
  777. for(c_next = clients;
  778. c_next && c_next != c->prev
  779. && c_next->tag != c->tag
  780. && c_next->screen != c->screen;
  781. c_next = c_next->next);
  782. if(c_next && c_next->tag == seltag[selscreen]
  783. && c_next->screen == selscreen)
  784. client_focus(c_next);
  785. free(c);
  786. return;
  787. }
  788. /** Unmap a client
  789. * \param c Client pointer
  790. */
  791. void
  792. client_unmap(Client *c)
  793. {
  794. CHECK(c);
  795. if(c->flags & FSSFlag)
  796. XUnmapWindow(dpy, c->win);
  797. else
  798. {
  799. if(TBARH - BORDH)
  800. {
  801. barwin_unmap_subwin(c->titlebar);
  802. barwin_unmap(c->titlebar);
  803. }
  804. XUnmapWindow(dpy, c->frame);
  805. XUnmapSubwindows(dpy, c->frame);
  806. c->flags |= UnmapFlag;
  807. }
  808. return;
  809. }
  810. /** Set the client screen
  811. *\param c Client pointer
  812. *\param s Screen id
  813. */
  814. void
  815. client_set_screen(Client *c, int s)
  816. {
  817. int os;
  818. XRectangle geo;
  819. if(!c || s < 0 || s > screen_count() - 1 || s == c->screen)
  820. return;
  821. /* Save old client screen/geo to arrange */
  822. geo = c->geo;
  823. os = c->screen;
  824. c->screen = s;
  825. c->tag = seltag[s];
  826. /* Arrange */
  827. if(tags[s][seltag[s]].layout.func == freelayout
  828. || tags[s][seltag[s]].layout.func == maxlayout
  829. || tags[os][seltag[os]].layout.func == freelayout
  830. || tags[os][seltag[os]].layout.func == maxlayout)
  831. {
  832. geo.x = (sgeo[s].x + sgeo[s].width / 2) - (c->geo.width / 2);
  833. geo.y = (sgeo[s].y + sgeo[s].height / 2) - (c->geo.height / 2);
  834. client_moveresize(c, geo, False);
  835. }
  836. arrange(s, True);
  837. arrange(os, True);
  838. if(!(c->flags & TileFlag))
  839. {
  840. client_focus(c);
  841. client_raise(c);
  842. }
  843. return;
  844. }
  845. /** Change client of screen to next screen
  846. * \param cmd uicb_t type unused
  847. */
  848. void
  849. uicb_client_screen_next(uicb_t cmd)
  850. {
  851. CHECK(sel);
  852. client_set_screen(sel, (sel->screen + 1 > screen_count() - 1) ? 0 : sel->screen + 1);
  853. return;
  854. }
  855. /** Change client of screen to prev screen
  856. * \param cmd uicb_t type unused
  857. */
  858. void
  859. uicb_client_screen_prev(uicb_t cmd)
  860. {
  861. CHECK(sel);
  862. client_set_screen(sel, (sel->screen - 1 < 0) ? screen_count() - 1 : sel->screen - 1);
  863. return;
  864. }
  865. /** Move a client
  866. *\param cmd uicb_t type
  867. */
  868. void
  869. uicb_client_move(uicb_t cmd)
  870. {
  871. XRectangle geo;
  872. int xi = 0, yi = 0;
  873. if((sel->flags & TileFlag)
  874. || (sel->flags & MaxFlag)
  875. || (sel->flags & LMaxFlag)
  876. || (sel->flags & FSSFlag)
  877. || !sel)
  878. return;
  879. geo = sel->geo;
  880. if(sscanf(cmd, "%d %d", &xi, &yi))
  881. {
  882. geo.x += xi;
  883. geo.y += yi;
  884. client_moveresize(sel, geo, False);
  885. }
  886. return;
  887. }
  888. /** Resize a client
  889. *\param cmd uicb_t type
  890. */
  891. void
  892. uicb_client_resize(uicb_t cmd)
  893. {
  894. XRectangle geo;
  895. int wi = 0, hi = 0;
  896. if((sel->flags & TileFlag)
  897. || (sel->flags & MaxFlag)
  898. || (sel->flags & LMaxFlag)
  899. || (sel->flags & FSSFlag)
  900. || !sel)
  901. return;
  902. geo = sel->geo;
  903. if(sscanf(cmd, "%d %d", &wi, &hi))
  904. {
  905. geo.width += ((geo.width + wi > sel->minw && geo.width + wi < 65536) ? wi : 0);
  906. geo.height += ((geo.height + hi > sel->minh && geo.height + hi < 65536) ? hi : 0);
  907. client_moveresize(sel, geo, False);
  908. }
  909. return;
  910. }