4 * Copyright (c) Tuomo Valkonen 1999-2008.
6 * See the included file LICENSE for details.
11 #include <libtu/obj.h>
12 #include <libtu/objp.h>
13 #include <libtu/minmax.h>
14 #include <libtu/map.h>
15 #include <libmainloop/defer.h>
29 #include "frame-pointer.h"
30 #include "frame-draw.h"
38 #include "framedpholder.h"
42 extern bool frame_set_background(WFrame *frame, bool set_always);
43 extern void frame_initialise_gr(WFrame *frame);
45 static bool frame_initialise_titles(WFrame *frame);
46 static void frame_free_titles(WFrame *frame);
48 static void frame_add_mode_bindmaps(WFrame *frame);
51 WHook *frame_managed_changed_hook=NULL;
53 #define FORWARD_CWIN_RQGEOM(FRAME) framemode_is_floating(frame_mode(FRAME))
54 #define USE_MINMAX(FRAME) framemode_is_floating(frame_mode(FRAME))
55 #define DEST_EMPTY(FRAME) framemode_is_floating(frame_mode(FRAME))
58 WFrameMode framemode_unalt(WFrameMode mode)
60 if(mode==FRAME_MODE_UNKNOWN_ALT)
61 return FRAME_MODE_UNKNOWN;
62 else if(mode==FRAME_MODE_TILED_ALT)
63 return FRAME_MODE_TILED;
64 else if(mode==FRAME_MODE_FLOATING_ALT)
65 return FRAME_MODE_FLOATING;
66 else if(mode==FRAME_MODE_TRANSIENT_ALT)
67 return FRAME_MODE_TRANSIENT;
73 static WFrameMode framemode_is_floating(WFrameMode mode)
75 WFrameMode modea=framemode_unalt(mode);
77 return (modea==FRAME_MODE_FLOATING || modea==FRAME_MODE_TRANSIENT);
81 /*{{{ Destroy/create frame */
84 bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp,
94 frame->tab_dragged_idx=-1;
99 frame->tr_mode=GR_TRANSPARENCY_DEFAULT;
101 frame->bar_brush=NULL;
104 frame->bar_max_width_q=1.0;
106 gr_stylespec_init(&frame->baseattr);
108 if(!mplex_init((WMPlex*)frame, parent, fp))
111 frame_initialise_gr(frame);
112 frame_initialise_titles(frame);
114 region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap);
115 region_add_bindmap((WRegion*)frame, ioncore_mplex_bindmap);
117 frame_add_mode_bindmaps(frame);
119 mplex_managed_geom((WMPlex*)frame, &mg);
122 frame->flags|=FRAME_SHADED;
124 ((WRegion*)frame)->flags|=REGION_PLEASE_WARP;
130 WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode)
132 CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode));
136 void frame_deinit(WFrame *frame)
138 frame_free_titles(frame);
139 frame_release_brushes(frame);
140 gr_stylespec_unalloc(&frame->baseattr);
141 mplex_deinit((WMPlex*)frame);
148 /*{{{ Mode switching */
151 static void frame_add_mode_bindmaps(WFrame *frame)
153 WFrameMode modea=framemode_unalt(frame->mode);
155 if(modea==FRAME_MODE_FLOATING){
156 region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
157 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
158 region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
159 }else if(modea==FRAME_MODE_TRANSIENT){
160 region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
161 region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
163 /* mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT || mode==FRAME_MODE_UNKNOWN */
164 region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
165 region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
166 region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
171 void frame_set_mode(WFrame *frame, WFrameMode mode)
173 if(frame->mode==mode)
176 frame_clear_shape(frame);
178 frame_release_brushes(frame);
180 region_remove_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap);
181 region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap);
182 region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap);
183 region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap);
184 region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap);
188 frame_add_mode_bindmaps(frame);
190 frame_updategr(frame);
194 WFrameMode frame_mode(WFrame *frame)
200 static StringIntMap frame_modes[]={
201 {"unknown", FRAME_MODE_UNKNOWN},
202 {"unknown-alt", FRAME_MODE_UNKNOWN_ALT},
203 {"tiled", FRAME_MODE_TILED},
204 {"tiled-alt", FRAME_MODE_TILED_ALT},
205 {"floating", FRAME_MODE_FLOATING},
206 {"floating-alt", FRAME_MODE_FLOATING_ALT},
207 {"transient", FRAME_MODE_TRANSIENT},
208 {"transient-alt", FRAME_MODE_TRANSIENT_ALT},
217 EXTL_EXPORT_AS(WFrame, mode)
218 const char *frame_mode_extl(WFrame *frame)
220 return stringintmap_key(frame_modes, frame->mode, NULL);
225 * Set frame mode (one of
226 * \codestr{unknown}, \codestr{tiled}, \codestr{floating}, \codestr{transient},
227 * or any of these suffixed with \codestr{-alt}).
229 EXTL_EXPORT_AS(WFrame, set_mode)
230 bool frame_set_mode_extl(WFrame *frame, const char *modestr)
235 idx=stringintmap_ndx(frame_modes, modestr);
239 frame_set_mode(frame, frame_modes[idx].value);
251 int frame_tab_at_x(WFrame *frame, int x)
256 frame_bar_geom(frame, &bg);
258 if(x>=bg.x+bg.w || x<bg.x)
263 for(tab=0; tab<FRAME_MCOUNT(frame); tab++){
264 tx+=frame_nth_tab_w(frame, tab);
273 int frame_nth_tab_x(WFrame *frame, int n)
279 x+=frame_nth_tab_w(frame, i);
285 static int frame_nth_tab_w_iw(WFrame *frame, int n, bool inner)
288 GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT;
289 int m=FRAME_MCOUNT(frame);
292 frame_bar_geom(frame, &bg);
297 if(frame->bar_brush!=NULL)
298 grbrush_get_border_widths(frame->bar_brush, &bdw);
301 w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1);
306 /* Get n:th tab's portion of free area */
307 w=(((n+1)*w)/m-(n*w)/m);
309 /* Add n:th tab's borders back */
311 w+=(n==0 ? bdw.left : bdw.tb_ileft);
312 w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing);
319 int frame_nth_tab_w(WFrame *frame, int n)
321 return frame_nth_tab_w_iw(frame, n, FALSE);
325 int frame_nth_tab_iw(WFrame *frame, int n)
327 return frame_nth_tab_w_iw(frame, n, TRUE);
332 void frame_update_attr_nth(WFrame *frame, int i)
336 if(i<0 || i>=frame->titles_n)
339 frame_update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i));
343 static void frame_update_attrs(WFrame *frame)
349 FRAME_MX_FOR_ALL(sub, frame, tmp){
350 frame_update_attr(frame, i, sub);
356 static void frame_free_titles(WFrame *frame)
360 if(frame->titles!=NULL){
361 for(i=0; i<frame->titles_n; i++){
362 if(frame->titles[i].text)
363 free(frame->titles[i].text);
364 gr_stylespec_unalloc(&frame->titles[i].attr);
373 static void do_init_title(WFrame *frame, int i, WRegion *sub)
375 frame->titles[i].text=NULL;
376 frame->titles[i].iw=frame_nth_tab_iw(frame, i);
378 gr_stylespec_init(&frame->titles[i].attr);
380 frame_update_attr(frame, i, sub);
384 static bool frame_initialise_titles(WFrame *frame)
386 int i, n=FRAME_MCOUNT(frame);
388 frame_free_titles(frame);
393 frame->titles=ALLOC_N(GrTextElem, n);
394 if(frame->titles==NULL)
398 if(FRAME_MCOUNT(frame)==0){
399 do_init_title(frame, 0, NULL);
404 FRAME_MX_FOR_ALL(sub, frame, tmp){
405 do_init_title(frame, i, sub);
410 frame_recalc_bar(frame, FALSE);
419 /*{{{ Resize and reparent */
422 bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp)
424 WRectangle old_geom, mg;
425 bool wchg=(REGION_GEOM(frame).w!=fp->g.w);
426 bool hchg=(REGION_GEOM(frame).h!=fp->g.h);
428 old_geom=REGION_GEOM(frame);
430 if(!window_fitrep(&(frame->mplex.win), par, fp))
433 mplex_managed_geom((WMPlex*)frame, &mg);
437 frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT);
438 frame->saved_y=old_geom.y;
439 frame->saved_h=old_geom.h;
441 frame->flags&=~FRAME_SHADED;
443 frame->flags&=~FRAME_MAXED_VERT;
448 frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ);
449 frame->saved_x=old_geom.x;
450 frame->saved_w=old_geom.w;
452 frame->flags&=~FRAME_MIN_HORIZ;
454 frame->flags&=~FRAME_MAXED_HORIZ;
458 mplex_fit_managed((WMPlex*)frame);
459 mplex_size_changed((WMPlex*)frame, wchg, hchg);
466 void frame_size_hints(WFrame *frame, WSizeHints *hints_ret)
473 mplex_managed_geom((WMPlex*)frame, &subgeom);
475 woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0);
476 hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0);
478 if(FRAME_CURRENT(frame)!=NULL)
479 region_size_hints(FRAME_CURRENT(frame), hints_ret);
481 sizehints_clear(hints_ret);
483 FRAME_MX_FOR_ALL(sub, frame, tmp){
484 sizehints_adjust_for(hints_ret, sub);
487 if(!USE_MINMAX(frame)){
488 hints_ret->max_set=0;
489 hints_ret->min_set=0;
490 /*hints_ret->base_set=0;*/
491 hints_ret->aspect_set=0;
492 hints_ret->no_constrain=FALSE;
493 /*hints_ret->no_constrain=TRUE;*/
496 if(!hints_ret->min_set){
497 hints_ret->min_width=0;
498 hints_ret->min_height=0;
499 hints_ret->min_set=TRUE;
502 if(!hints_ret->base_set){
503 hints_ret->base_width=0;
504 hints_ret->base_height=0;
505 hints_ret->base_set=TRUE;
508 hints_ret->base_width+=woff;
509 hints_ret->base_height+=hoff;
510 hints_ret->max_width+=woff;
511 hints_ret->max_height+=hoff;
512 hints_ret->min_width+=woff;
513 hints_ret->min_height+=hoff;
516 int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE);
518 if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){
519 int h=frame_shaded_height(frame);
520 hints_ret->min_height=h;
521 hints_ret->max_height=h;
522 hints_ret->base_height=h;
523 if(!hints_ret->max_set){
524 hints_ret->max_width=INT_MAX;
525 hints_ret->max_set=TRUE;
535 /*{{{ Client window rqgeom */
538 static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub,
539 const WRQGeomParams *rq,
542 if(!FORWARD_CWIN_RQGEOM(frame)){
543 region_managed_rqgeom_absolute_default((WRegion*)frame, sub,
546 WRQGeomParams rq2=RQGEOMPARAMS_INIT;
547 int gravity=ForgetGravity;
552 rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL
553 |REGION_RQGEOM_TRYONLY
554 |REGION_RQGEOM_ABSOLUTE);
556 if(rq->flags®ION_RQGEOM_GRAVITY)
559 mplex_managed_geom(&frame->mplex, &off);
562 off.w=REGION_GEOM(frame).w-off.w;
563 off.h=REGION_GEOM(frame).h-off.h;
565 rq2.geom.w=maxof(rq2.geom.w+off.w, 0);
566 rq2.geom.h=maxof(rq2.geom.h+off.h, 0);
568 /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/
570 /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not
571 * requested by the client and clientwin_handle_configure_request has
572 * no better guess. Otherwise the coordinates are those requested by
573 * the client (modulo borders/gravity) and we interpret them to be
574 * root-relative coordinates for this frame modulo gravity.
576 if(rq->flags®ION_RQGEOM_WEAK_X)
579 rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w);
581 if(rq->flags®ION_RQGEOM_WEAK_Y)
584 rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h);
586 region_rqgeom((WRegion*)frame, &rq2, geomret);
601 /*{{{ Frame recreate pholder stuff */
604 static WFramedPHolder *frame_make_recreate_pholder(WFrame *frame, WPHolder *rph)
606 WFramedParam fparam=FRAMEDPARAM_INIT;
607 WFramedPHolder *fph=NULL;
610 fparam.mode=frame->mode;
612 fph=create_framedpholder(rph, &fparam);
615 destroy_obj((Obj*)rph);
622 static void mplex_migrate_phs_to_fph(WMPlex *mplex, WFramedPHolder *fph)
624 WMPlexPHolder *phs, *ph;
627 mplex->misc_phs=NULL;
629 phs->recreate_pholder=fph;
631 for(ph=phs; ph!=NULL; ph=ph->next)
637 bool frame_rescue_clientwins(WFrame *frame, WRescueInfo *info)
641 ret=mplex_rescue_clientwins(&frame->mplex, info);
643 /* Now, do placeholders.
644 * We can't currently be arsed to keep them ordered with regions...
645 * First we check if we can simply recreate this frame, which takes
646 * care of e.g. floating frames being destroyed when entering full
647 * screen mode. If not, we check if the target would be a WMPlex, and
648 * migrate there. This takes care of frames destroyed in tilings.
650 mplex_flatten_phs(&frame->mplex);
652 if(frame->mplex.misc_phs!=NULL){
653 WPHolder *ret_ph=region_make_return_pholder((WRegion*)frame);
654 WFramedPHolder *fph=frame_make_recreate_pholder(frame, ret_ph);
657 mplex_migrate_phs_to_fph(&frame->mplex, fph);
659 WPHolder *rescueph=rescueinfo_pholder(info);
661 WRegion *target=pholder_target(rescueph);
662 WMPlex *other_mplex=OBJ_CAST(target, WMPlex);
664 if(other_mplex!=NULL){
665 assert(other_mplex!=&frame->mplex);
666 mplex_migrate_phs(&frame->mplex, other_mplex);
682 bool frame_set_shaded(WFrame *frame, int sp)
684 bool set=(frame->flags&FRAME_SHADED);
685 bool nset=libtu_do_setparam(sp, set);
686 WRQGeomParams rq=RQGEOMPARAMS_INIT;
693 rq.flags=REGION_RQGEOM_H_ONLY;
694 rq.geom=REGION_GEOM(frame);
697 if(!(frame->flags&FRAME_SAVED_VERT))
699 rq.geom.h=frame->saved_h;
701 if(frame->barmode==FRAME_BAR_NONE)
703 rq.geom.h=frame_shaded_height(frame);
706 frame->flags|=FRAME_SHADED_TOGGLE;
708 region_rqgeom((WRegion*)frame, &rq, NULL);
710 frame->flags&=~FRAME_SHADED_TOGGLE;
712 return (frame->flags&FRAME_SHADED);
717 * Set shading state according to the parameter \var{how}
718 * (\codestr{set}, \codestr{unset}, or \codestr{toggle}).
719 * Resulting state is returned, which may not be
720 * what was requested.
722 EXTL_EXPORT_AS(WFrame, set_shaded)
723 bool frame_set_shaded_extl(WFrame *frame, const char *how)
725 return frame_set_shaded(frame, libtu_string_to_setparam(how));
730 * Is \var{frame} shaded?
734 bool frame_is_shaded(WFrame *frame)
736 return ((frame->flags&FRAME_SHADED)!=0);
741 * Is the attribute \var{attr} set?
743 bool frame_is_grattr(WFrame *frame, const char *attr)
745 GrAttr a=stringstore_alloc(attr);
746 bool set=gr_stylespec_isset(&frame->baseattr, a);
752 bool frame_set_grattr(WFrame *frame, GrAttr a, int sp)
754 bool set=gr_stylespec_isset(&frame->baseattr, a);
755 bool nset=libtu_do_setparam(sp, set);
759 gr_stylespec_set(&frame->baseattr, a);
761 gr_stylespec_unset(&frame->baseattr, a);
762 window_draw((WWindow*)frame, TRUE);
770 * Set extra drawing engine attributes for the frame.
771 * The parameter \var{attr} is the attribute, and \var{how} is
772 * one of \codestr{set}, \codestr{unset}, or \codestr{toggle}.
774 EXTL_EXPORT_AS(WFrame, set_grattr)
775 bool frame_set_grattr_extl(WFrame *frame, const char *attr, const char *how)
778 GrAttr a=stringstore_alloc(attr);
779 bool ret=frame_set_grattr(frame, a, libtu_string_to_setparam(how));
788 void frame_managed_notify(WFrame *frame, WRegion *sub, WRegionNotify how)
790 if(how==ioncore_g.notifies.activated ||
791 how==ioncore_g.notifies.inactivated ||
792 how==ioncore_g.notifies.name ||
793 how==ioncore_g.notifies.activity ||
794 how==ioncore_g.notifies.sub_activity ||
795 how==ioncore_g.notifies.tag){
797 frame_update_attrs(frame);
798 frame_recalc_bar(frame, FALSE);
799 frame_draw_bar(frame, FALSE);
804 static void frame_size_changed_default(WFrame *frame,
805 bool wchg, bool hchg)
807 int bar_w=frame->bar_w;
810 frame_recalc_bar(frame, TRUE);
812 if(frame->barmode==FRAME_BAR_SHAPED &&
813 ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){
814 frame_set_shape(frame);
819 static void frame_managed_changed(WFrame *frame, int mode, bool sw,
824 if(mode!=MPLEX_CHANGE_SWITCHONLY)
825 frame_initialise_titles(frame);
827 frame_update_attrs(frame);
830 need_draw=!frame_set_background(frame, FALSE);
833 frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY);
835 mplex_call_changed_hook((WMPlex*)frame,
836 frame_managed_changed_hook,
841 WRegion *frame_managed_disposeroot(WFrame *frame, WRegion *reg)
843 if(DEST_EMPTY(frame) &&
844 frame->mplex.mgd!=NULL &&
845 frame->mplex.mgd->reg==reg &&
846 frame->mplex.mgd->mgr_next==NULL){
847 WRegion *tmp=region_disposeroot((WRegion*)frame);
848 return (tmp!=NULL ? tmp : reg);
855 int frame_default_index(WFrame *frame)
857 return ioncore_g.frame_default_index;
864 /*{{{ prepare_manage_transient */
867 WPHolder *frame_prepare_manage_transient(WFrame *frame,
868 const WClientWin *transient,
869 const WManageParams *param,
872 /* Transient manager searches should not cross tiled frames
873 * unless explicitly floated.
875 if(framemode_is_floating(frame_mode(frame)) ||
876 extl_table_is_bool_set(transient->proptab, "float")){
877 return region_prepare_manage_transient_default((WRegion*)frame,
893 ExtlTab frame_get_configuration(WFrame *frame)
895 ExtlTab tab=mplex_get_configuration(&frame->mplex);
897 extl_table_sets_i(tab, "mode", frame->mode);
899 if(frame->flags&FRAME_SAVED_VERT){
900 extl_table_sets_i(tab, "saved_y", frame->saved_y);
901 extl_table_sets_i(tab, "saved_h", frame->saved_h);
904 if(frame->flags&FRAME_SAVED_HORIZ){
905 extl_table_sets_i(tab, "saved_x", frame->saved_x);
906 extl_table_sets_i(tab, "saved_w", frame->saved_w);
914 void frame_do_load(WFrame *frame, ExtlTab tab)
919 if(extl_table_gets_i(tab, "saved_x", &p) &&
920 extl_table_gets_i(tab, "saved_w", &s)){
923 frame->flags|=FRAME_SAVED_HORIZ;
926 if(extl_table_gets_i(tab, "saved_y", &p) &&
927 extl_table_gets_i(tab, "saved_h", &s)){
930 frame->flags|=FRAME_SAVED_VERT;
933 mplex_load_contents(&frame->mplex, tab);
937 WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
939 int mode=FRAME_MODE_UNKNOWN;
942 extl_table_gets_i(tab, "mode", &mode);
944 frame=create_frame(par, fp, mode);
947 frame_do_load(frame, tab);
949 if(DEST_EMPTY(frame) && frame->mplex.mgd==NULL){
950 if(frame->mplex.misc_phs!=NULL){
951 /* Session management hack */
953 fph=frame_make_recreate_pholder(frame, ioncore_get_load_pholder());
955 mplex_migrate_phs_to_fph(&frame->mplex, fph);
958 destroy_obj((Obj*)frame);
962 return (WRegion*)frame;
969 /*{{{ Dynfuntab and class info */
972 static DynFunTab frame_dynfuntab[]={
973 {region_size_hints, frame_size_hints},
975 {mplex_managed_changed, frame_managed_changed},
976 {mplex_size_changed, frame_size_changed_default},
977 {region_managed_notify, frame_managed_notify},
979 {region_activated, frame_activated},
980 {region_inactivated, frame_inactivated},
982 {(DynFun*)window_press, (DynFun*)frame_press},
984 {(DynFun*)region_get_configuration,
985 (DynFun*)frame_get_configuration},
996 {(DynFun*)region_fitrep,
997 (DynFun*)frame_fitrep},
999 {(DynFun*)region_managed_disposeroot,
1000 (DynFun*)frame_managed_disposeroot},
1002 {region_managed_rqgeom_absolute,
1003 frame_managed_rqgeom_absolute},
1005 {(DynFun*)mplex_default_index,
1006 (DynFun*)frame_default_index},
1008 {(DynFun*)region_prepare_manage_transient,
1009 (DynFun*)frame_prepare_manage_transient},
1011 {(DynFun*)region_rescue_clientwins,
1012 (DynFun*)frame_rescue_clientwins},
1019 IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab);