client.c 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  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. if(conf.bars.selbar)
  225. infobar_draw_selbar(sel->screen);
  226. }
  227. else
  228. {
  229. XSetInputFocus(dpy, ROOT, RevertToPointerRoot, CurrentTime);
  230. if(conf.bars.selbar)
  231. infobar_draw_selbar(selscreen);
  232. }
  233. return;
  234. }
  235. /* The following function have the same point :
  236. * find a client member with a Window {{{
  237. */
  238. /* Get Client with a window */
  239. /** Get a client->win with a window
  240. * \param w Window
  241. * \return The client
  242. */
  243. Client* client_gb_win(Window w)
  244. {
  245. Client *c;
  246. for(c = clients; c && c->win != w; c = c->next);
  247. return c;
  248. }
  249. /** Get a client->frame with a window
  250. * \param w Window
  251. * \return The client
  252. */
  253. Client* client_gb_frame(Window w)
  254. {
  255. Client *c;
  256. for(c = clients; c && c->frame != w; c = c->next);
  257. return c;
  258. }
  259. /** Get a client->titlebar with a window
  260. * \param w Window
  261. * \return The client
  262. */
  263. Client* client_gb_titlebar(Window w)
  264. {
  265. Client *c;
  266. if(!(TBARH - BORDH))
  267. return NULL;
  268. for(c = clients; c && c->titlebar->win != w; c = c->next);
  269. return c;
  270. }
  271. /** Get a client->resize with a window
  272. * \param w Window
  273. * \return The client
  274. */
  275. Client* client_gb_resize(Window w)
  276. {
  277. Client *c;
  278. for(c = clients; (c && c->resize[Right] != w) && (c && c->resize[Left] != w); c = c->next);
  279. return c;
  280. }
  281. /** Get a client->button[button_num] with a window
  282. * \param w Window
  283. * \param n Pointer who return the button_num
  284. * \return The client
  285. */
  286. Client* client_gb_button(Window w, int *n)
  287. {
  288. Client *c;
  289. int i;
  290. if(!BUTTONWH || !(TBARH - BORDH))
  291. return NULL;
  292. for(c = clients; c; c = c->next)
  293. for(i = 0; i < conf.titlebar.nbutton; ++i)
  294. if(c->button[i] == w)
  295. {
  296. *n = i;
  297. return c;
  298. }
  299. return NULL;
  300. }
  301. /* }}} */
  302. /** Get a client name
  303. * \param c Client pointer
  304. */
  305. void
  306. client_get_name(Client *c)
  307. {
  308. Atom rt;
  309. int rf;
  310. ulong ir, il;
  311. /* This one instead XFetchName for utf8 name support */
  312. if(XGetWindowProperty(dpy, c->win, net_atom[net_wm_name], 0, 4096,
  313. False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title) != Success)
  314. XGetWindowProperty(dpy, c->win, ATOM("WM_NAME"), 0, 4096,
  315. False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title);
  316. /* If there is no title... */
  317. if(!c->title)
  318. {
  319. XFetchName(dpy, c->win, &(c->title));
  320. if(!c->title)
  321. c->title = _strdup("WMFS");
  322. }
  323. frame_update(c);
  324. if(conf.bars.selbar && c == sel)
  325. infobar_draw_selbar(c->screen);
  326. return;
  327. }
  328. /** Hide a client (for tag switching)
  329. * \param c Client pointer
  330. */
  331. void
  332. client_hide(Client *c)
  333. {
  334. CHECK(!(c->flags & HideFlag));
  335. client_unmap(c);
  336. c->flags |= HideFlag;
  337. setwinstate(c->win, IconicState);
  338. return;
  339. }
  340. /** Check if the client 'c' is hide
  341. * \param c Client pointer
  342. * \return True if the client is hide; False if not
  343. */
  344. Bool
  345. ishide(Client *c, int screen)
  346. {
  347. if(c->tag == seltag[screen] && c->screen == screen)
  348. return False;
  349. return True;
  350. }
  351. /** Kill a client
  352. * \param c Client pointer
  353. */
  354. void
  355. client_kill(Client *c)
  356. {
  357. XEvent ev;
  358. Atom *atom = NULL;
  359. int proto;
  360. int canbedel = 0;
  361. CHECK(c);
  362. if(XGetWMProtocols(dpy, c->win, &atom, &proto) && atom)
  363. {
  364. while(proto--)
  365. if(atom[proto] == ATOM("WM_DELETE_WINDOW"))
  366. ++canbedel;
  367. XFree(atom);
  368. if(canbedel)
  369. {
  370. ev.type = ClientMessage;
  371. ev.xclient.window = c->win;
  372. ev.xclient.message_type = ATOM("WM_PROTOCOLS");
  373. ev.xclient.format = 32;
  374. ev.xclient.data.l[0] = ATOM("WM_DELETE_WINDOW");
  375. ev.xclient.data.l[1] = CurrentTime;
  376. ev.xclient.data.l[2] = 0;
  377. ev.xclient.data.l[3] = 0;
  378. ev.xclient.data.l[4] = 0;
  379. XSendEvent(dpy, c->win, False, NoEventMask, &ev);
  380. }
  381. else
  382. XKillClient(dpy, c->win);
  383. }
  384. else
  385. XKillClient(dpy, c->win);
  386. return;
  387. }
  388. /** Kill the selected client
  389. * \param cmd uicb_t type unused
  390. */
  391. void
  392. uicb_client_kill(uicb_t cmd)
  393. {
  394. CHECK(sel);
  395. client_kill(sel);
  396. return;
  397. }
  398. /** Map a client
  399. * \param c Client pointer
  400. */
  401. void
  402. client_map(Client *c)
  403. {
  404. CHECK(c);
  405. if(c->flags & FSSFlag)
  406. XMapWindow(dpy, c->win);
  407. else
  408. {
  409. XMapWindow(dpy, c->frame);
  410. XMapSubwindows(dpy, c->frame);
  411. if(TBARH - BORDH)
  412. {
  413. barwin_map(c->titlebar);
  414. barwin_map_subwin(c->titlebar);
  415. }
  416. XMapSubwindows(dpy, c->frame);
  417. c->flags &= ~UnmapFlag;
  418. }
  419. return;
  420. }
  421. /** Manage a client with a window and his attributes
  422. * \param w Cient's futur Window
  423. * \param wa XWindowAttributes pointer, Window w attributes
  424. * \param ar Do arrange() or not (Bool)
  425. * \return The managed client
  426. */
  427. Client*
  428. client_manage(Window w, XWindowAttributes *wa, Bool ar)
  429. {
  430. Client *c, *t = NULL;
  431. Window trans, dw;
  432. Status rettrans;
  433. XSetWindowAttributes at;
  434. int mx, my, dint;
  435. uint duint;
  436. screen_get_sel();
  437. c = emalloc(1, sizeof(Client));
  438. c->win = w;
  439. c->screen = selscreen;
  440. c->flags = 0;
  441. XQueryPointer(dpy, ROOT, &dw, &dw, &mx, &my, &dint, &dint, &duint);
  442. if(conf.client.place_at_mouse)
  443. {
  444. mx += BORDH;
  445. my += TBARH;
  446. if((MAXW - mx) < wa->width)
  447. mx -= wa->width + BORDH;
  448. if((MAXH - my) < wa->height)
  449. my -= wa->height + TBARH;
  450. }
  451. else
  452. {
  453. mx = wa->x + BORDH;
  454. my = wa->y + TBARH + INFOBARH;
  455. /* Check if the client is already in the selected
  456. * screen, else place the client in it */
  457. if(screen_get_with_geo(mx, my) != selscreen)
  458. {
  459. mx += spgeo[selscreen].x;
  460. my += spgeo[selscreen].y;
  461. }
  462. }
  463. c->ogeo.x = c->geo.x = mx;
  464. c->ogeo.y = c->geo.y = my;
  465. c->ogeo.width = c->geo.width = wa->width;
  466. c->ogeo.height = c->geo.height = wa->height;
  467. c->free_geo = c->geo;
  468. c->tag = seltag[c->screen];
  469. c->layer = (sel && sel->layer > 0) ? sel->layer : 1;
  470. at.event_mask = PropertyChangeMask;
  471. frame_create(c);
  472. client_size_hints(c);
  473. XChangeWindowAttributes(dpy, c->win, CWEventMask, &at);
  474. XSetWindowBorderWidth(dpy, c->win, 0); /* client win sould _not_ have borders */
  475. mouse_grabbuttons(c, False);
  476. if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
  477. for(t = clients; t && t->win != trans; t = t->next);
  478. if(t)
  479. c->tag = t->tag;
  480. if(!(c->flags & FreeFlag)
  481. && (rettrans == Success || (c->flags & HintFlag)))
  482. c->flags |= FreeFlag;
  483. free(t);
  484. client_attach(c);
  485. client_get_name(c);
  486. client_map(c);
  487. client_raise(c);
  488. client_focus(c);
  489. setwinstate(c->win, NormalState);
  490. ewmh_get_client_list();
  491. ewmh_manage_window_type(c);
  492. client_set_wanted_tag(c);
  493. client_update_attributes(c);
  494. if(ar)
  495. arrange(c->screen, True);
  496. if(!conf.client.set_new_win_master)
  497. layout_set_client_master(c);
  498. return c;
  499. }
  500. /** Set a geometry with the client size hints
  501. *\param geo Geometry pointer
  502. *\param c Client pointer
  503. */
  504. void
  505. client_geo_hints(XRectangle *geo, Client *c)
  506. {
  507. /* minimum possible */
  508. if(geo->width < 1)
  509. geo->width = 1;
  510. if(geo->height < 1)
  511. geo->height = 1;
  512. /* base */
  513. geo->width -= c->basew;
  514. geo->height -= c->baseh;
  515. /* aspect */
  516. if(c->minay > 0 && c->maxay > 0
  517. && c->minax > 0 && c->maxax > 0)
  518. {
  519. if(geo->width * c->maxay > geo->height * c->maxax)
  520. geo->width = geo->height * c->maxax / c->maxay;
  521. else if(geo->width * c->minay < geo->height * c->minax)
  522. geo->height = geo->width * c->minay / c->minax;
  523. }
  524. /* incremental */
  525. if(c->incw)
  526. geo->width -= geo->width % c->incw;
  527. if(c->inch)
  528. geo->height -= geo->height % c->inch;
  529. /* base dimension */
  530. geo->width += c->basew;
  531. geo->height += c->baseh;
  532. if(c->minw > 0 && geo->width < c->minw)
  533. geo->width = c->minw;
  534. if(c->minh > 0 && geo->height < c->minh)
  535. geo->height = c->minh;
  536. if(c->maxw > 0 && geo->width > c->maxw)
  537. geo->width = c->maxw;
  538. if(c->maxh > 0 && geo->height > c->maxh)
  539. geo->height = c->maxh;
  540. return;
  541. }
  542. /** Move and Resize a client
  543. * \param c Client pointer
  544. * \param geo Coordinate info for the future size
  545. * of the client
  546. * \param r Bool for resize hint or not
  547. */
  548. void
  549. client_moveresize(Client *c, XRectangle geo, Bool r)
  550. {
  551. int os;
  552. if(!c)
  553. return;
  554. os = c->screen;
  555. if(r)
  556. client_geo_hints(&geo, c);
  557. c->flags &= ~MaxFlag;
  558. c->geo = c->ogeo = geo;
  559. if((c->screen = screen_get_with_geo(c->geo.x, c->geo.y)) != os)
  560. c->tag = seltag[c->screen];
  561. frame_moveresize(c, c->geo);
  562. XMoveResizeWindow(dpy, c->win, BORDH, TBARH, c->geo.width, c->geo.height);
  563. client_configure(c);
  564. client_update_attributes(c);
  565. return;
  566. }
  567. /** Maximize a client
  568. * \param c Client pointer
  569. */
  570. void
  571. client_maximize(Client *c)
  572. {
  573. XRectangle geo;
  574. if(!c || c->flags & FSSFlag)
  575. return;
  576. c->screen = screen_get_with_geo(c->geo.x, c->geo.y);
  577. geo.x = sgeo[c->screen].x;
  578. geo.y = sgeo[c->screen].y;
  579. geo.width = sgeo[c->screen].width - BORDH * 2;
  580. geo.height = sgeo[c->screen].height - BORDH;
  581. client_moveresize(c, geo, False);
  582. return;
  583. }
  584. /** Get client size hints
  585. * \param c Client pointer
  586. */
  587. void
  588. client_size_hints(Client *c)
  589. {
  590. long msize;
  591. XSizeHints size;
  592. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  593. size.flags = PSize;
  594. /* base */
  595. if(size.flags & PBaseSize)
  596. {
  597. c->basew = size.base_width;
  598. c->baseh = size.base_height;
  599. }
  600. else if(size.flags & PMinSize)
  601. {
  602. c->basew = size.min_width;
  603. c->baseh = size.min_height;
  604. }
  605. else
  606. c->basew = c->baseh = 0;
  607. /* inc */
  608. if(size.flags & PResizeInc)
  609. {
  610. c->incw = size.width_inc;
  611. c->inch = size.height_inc;
  612. }
  613. else
  614. c->incw = c->inch = 0;
  615. /* max */
  616. if(size.flags & PMaxSize)
  617. {
  618. c->maxw = size.max_width;
  619. c->maxh = size.max_height;
  620. }
  621. else
  622. c->maxw = c->maxh = 0;
  623. /* min */
  624. if(size.flags & PMinSize)
  625. {
  626. c->minw = (size.min_width ? size.min_width : 1);
  627. c->minh = (size.min_height ? size.min_height: 1);
  628. }
  629. else if(size.flags & PBaseSize)
  630. {
  631. c->minw = (size.base_width ? size.base_width : 1);
  632. c->minh = (size.base_height ? size.base_height : 1);
  633. }
  634. else
  635. c->minw = c->minh = 0;
  636. /* aspect */
  637. if(size.flags & PAspect)
  638. {
  639. c->minax = size.min_aspect.x;
  640. c->maxax = size.max_aspect.x;
  641. c->minay = size.min_aspect.y;
  642. c->maxay = size.max_aspect.y;
  643. }
  644. else
  645. c->minax = c->maxax = c->minay = c->maxay = 0;
  646. if(c->maxw && c->minw && c->maxh && c->minh
  647. && c->maxw == c->minw && c->maxh == c->minh)
  648. c->flags |= HintFlag;
  649. return;
  650. }
  651. /** Swap two clients
  652. *\param 1 c1 First client
  653. *\param 2 c2 Second client
  654. */
  655. void
  656. client_swap(Client *c1, Client *c2)
  657. {
  658. /* Check if no one of these clients are free */
  659. CHECK(!(c1->flags & FreeFlag));
  660. CHECK(!(c2->flags & FreeFlag));
  661. if(c1 == c2 || (c1->screen == c2->screen && c1->tag != c2->tag))
  662. return;
  663. /* Swap only the windows */
  664. swap_ptr((void**)&c1->win, (void**)&c2->win);
  665. /* Re-adapt the windows position with its new frame */
  666. XReparentWindow(dpy, c1->win, c1->frame, BORDH, TBARH);
  667. XReparentWindow(dpy, c2->win, c2->frame, BORDH, TBARH);
  668. /* Re-set size hints properties */
  669. client_size_hints(c1);
  670. client_size_hints(c2);
  671. /* Resize the windows */
  672. client_moveresize(c1, c1->geo, False);
  673. client_moveresize(c2, c2->geo, False);
  674. /* Get the new client name */
  675. client_get_name(c1);
  676. client_get_name(c2);
  677. return;
  678. }
  679. /** Set the wanted tag of a client
  680. *\param c Client pointer
  681. */
  682. void
  683. client_set_wanted_tag(Client *c)
  684. {
  685. XClassHint xch = { 0 };
  686. int i, j, k;
  687. if(conf.ignore_next_client_rules)
  688. {
  689. conf.ignore_next_client_rules = False;
  690. return;
  691. }
  692. XGetClassHint(dpy, c->win, &xch);
  693. for(i = 0; i < screen_count(); ++i)
  694. for(j = 1; j < conf.ntag[i] + 1; ++j)
  695. if(tags[i][j].clients)
  696. for(k = 0; k < tags[i][j].nclients; ++k)
  697. if((xch.res_name && strstr(xch.res_name, tags[i][j].clients[k]))
  698. || (xch.res_class && strstr(xch.res_class, tags[i][j].clients[k])))
  699. {
  700. c->screen = i;
  701. c->tag = j;
  702. if(c->tag != seltag[selscreen])
  703. tags[c->screen][c->tag].request_update = True;
  704. tags[c->screen][c->tag].layout.func(c->screen);
  705. }
  706. return;
  707. }
  708. /** Update client attributes (_WMFS_TAG _WMFS_SCREEN)
  709. *\param c Client pointer
  710. */
  711. void
  712. client_update_attributes(Client *c)
  713. {
  714. Bool f;
  715. /* For reload use */
  716. XChangeProperty(dpy, c->win, ATOM("_WMFS_TAG"), XA_CARDINAL, 32,
  717. PropModeReplace, (uchar*)&(c->tag), 1);
  718. XChangeProperty(dpy, c->win, ATOM("_WMFS_SCREEN"), XA_CARDINAL, 32,
  719. PropModeReplace, (uchar*)&(c->screen), 1);
  720. f = (c->flags & FreeFlag) ? True : False;
  721. XChangeProperty(dpy, c->win, ATOM("_WMFS_ISFREE"), XA_CARDINAL, 32,
  722. PropModeReplace, (uchar*)&f, 1);
  723. return;
  724. }
  725. /** Raise a client
  726. * \param c Client pointer
  727. */
  728. void
  729. client_raise(Client *c)
  730. {
  731. if(!c || ((c->flags & TileFlag) && !(c->flags & AboveFlag)))
  732. return;
  733. XRaiseWindow(dpy, c->frame);
  734. return;
  735. }
  736. /** Raise the selected client
  737. * \param cmd uicb_t type unused
  738. */
  739. void
  740. uicb_client_raise(uicb_t cmd)
  741. {
  742. client_raise(sel);
  743. return;
  744. }
  745. /** UnHide a client (for tag switching)
  746. * \param c Client pointer
  747. */
  748. void
  749. client_unhide(Client *c)
  750. {
  751. CHECK(c->flags & HideFlag);
  752. client_map(c);
  753. c->flags &= ~HideFlag;
  754. setwinstate(c->win, NormalState);
  755. return;
  756. }
  757. /** Unmanage a client
  758. * \param c Client pointer
  759. */
  760. void
  761. client_unmanage(Client *c)
  762. {
  763. Client *c_next = NULL;
  764. Bool b = False;
  765. int i;
  766. XGrabServer(dpy);
  767. XSetErrorHandler(errorhandlerdummy);
  768. XReparentWindow(dpy, c->win, ROOT, c->geo.x, c->geo.y);
  769. if(sel == c)
  770. client_focus(NULL);
  771. client_detach(c);
  772. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  773. setwinstate(c->win, WithdrawnState);
  774. frame_delete(c);
  775. XSync(dpy, False);
  776. XUngrabServer(dpy);
  777. ewmh_get_client_list();
  778. /* Arrange */
  779. for(i = 0; i < screen_count() && !b; ++i)
  780. if(c->tag == seltag[i])
  781. b = True;
  782. if(b)
  783. tags[c->screen][c->tag].layout.func(c->screen);
  784. else
  785. {
  786. tags[c->screen][c->tag].request_update = True;
  787. infobar_draw(c->screen);
  788. }
  789. XFree(c->title);
  790. /* To focus the previous client */
  791. for(c_next = clients;
  792. c_next && c_next != c->prev
  793. && c_next->tag != c->tag
  794. && c_next->screen != c->screen;
  795. c_next = c_next->next);
  796. if(c_next && c_next->tag == seltag[selscreen]
  797. && c_next->screen == selscreen)
  798. client_focus(c_next);
  799. free(c);
  800. return;
  801. }
  802. /** Unmap a client
  803. * \param c Client pointer
  804. */
  805. void
  806. client_unmap(Client *c)
  807. {
  808. CHECK(c);
  809. if(c->flags & FSSFlag)
  810. XUnmapWindow(dpy, c->win);
  811. else
  812. {
  813. if(TBARH - BORDH)
  814. {
  815. barwin_unmap_subwin(c->titlebar);
  816. barwin_unmap(c->titlebar);
  817. }
  818. XUnmapWindow(dpy, c->frame);
  819. XUnmapSubwindows(dpy, c->frame);
  820. c->flags |= UnmapFlag;
  821. }
  822. return;
  823. }
  824. /** Set the client screen
  825. *\param c Client pointer
  826. *\param s Screen id
  827. */
  828. void
  829. client_set_screen(Client *c, int s)
  830. {
  831. int os;
  832. XRectangle geo;
  833. if(!c || s < 0 || s > screen_count() - 1 || s == c->screen)
  834. return;
  835. /* Save old client screen/geo to arrange */
  836. geo = c->geo;
  837. os = c->screen;
  838. c->screen = s;
  839. c->tag = seltag[s];
  840. /* Arrange */
  841. if(tags[s][seltag[s]].layout.func == freelayout
  842. || tags[s][seltag[s]].layout.func == maxlayout
  843. || tags[os][seltag[os]].layout.func == freelayout
  844. || tags[os][seltag[os]].layout.func == maxlayout)
  845. {
  846. geo.x = (sgeo[s].x + sgeo[s].width / 2) - (c->geo.width / 2);
  847. geo.y = (sgeo[s].y + sgeo[s].height / 2) - (c->geo.height / 2);
  848. client_moveresize(c, geo, False);
  849. }
  850. arrange(s, True);
  851. arrange(os, True);
  852. if(!(c->flags & TileFlag))
  853. {
  854. client_focus(c);
  855. client_raise(c);
  856. }
  857. return;
  858. }
  859. /** Change client of screen to next screen
  860. * \param cmd uicb_t type unused
  861. */
  862. void
  863. uicb_client_screen_next(uicb_t cmd)
  864. {
  865. CHECK(sel);
  866. client_set_screen(sel, (sel->screen + 1 > screen_count() - 1) ? 0 : sel->screen + 1);
  867. return;
  868. }
  869. /** Change client of screen to prev screen
  870. * \param cmd uicb_t type unused
  871. */
  872. void
  873. uicb_client_screen_prev(uicb_t cmd)
  874. {
  875. CHECK(sel);
  876. client_set_screen(sel, (sel->screen - 1 < 0) ? screen_count() - 1 : sel->screen - 1);
  877. return;
  878. }
  879. /** Move a client
  880. *\param cmd uicb_t type
  881. */
  882. void
  883. uicb_client_move(uicb_t cmd)
  884. {
  885. XRectangle geo;
  886. int xi = 0, yi = 0;
  887. if((sel->flags & TileFlag)
  888. || (sel->flags & MaxFlag)
  889. || (sel->flags & LMaxFlag)
  890. || (sel->flags & FSSFlag)
  891. || !sel)
  892. return;
  893. geo = sel->geo;
  894. if(sscanf(cmd, "%d %d", &xi, &yi))
  895. {
  896. geo.x += xi;
  897. geo.y += yi;
  898. client_moveresize(sel, geo, False);
  899. }
  900. return;
  901. }
  902. /** Resize a client
  903. *\param cmd uicb_t type
  904. */
  905. void
  906. uicb_client_resize(uicb_t cmd)
  907. {
  908. XRectangle geo;
  909. int wi = 0, hi = 0;
  910. if((sel->flags & TileFlag)
  911. || (sel->flags & MaxFlag)
  912. || (sel->flags & LMaxFlag)
  913. || (sel->flags & FSSFlag)
  914. || !sel)
  915. return;
  916. geo = sel->geo;
  917. if(sscanf(cmd, "%d %d", &wi, &hi))
  918. {
  919. geo.width += ((geo.width + wi > sel->minw && geo.width + wi < 65536) ? wi : 0);
  920. geo.height += ((geo.height + hi > sel->minh && geo.height + hi < 65536) ? hi : 0);
  921. client_moveresize(sel, geo, False);
  922. }
  923. return;
  924. }
  925. /** Ignore next client rules
  926. *\param cmd uicb_t type
  927. */
  928. void
  929. uicb_ignore_next_client_rules(uicb_t cmd)
  930. {
  931. conf.ignore_next_client_rules = !conf.ignore_next_client_rules;
  932. return;
  933. }