🔒 Repository is read-only – file editing is disabled.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168
diff -Naur a/configure.ac b/configure.ac
--- a/configure.ac 2015-07-01 11:21:15.000000000 +0200
+++ b/configure.ac 2023-04-07 20:15:49.260782004 +0200
@@ -103,6 +103,8 @@
AC_CHECK_HEADERS(signal.h string.h stdio.h stdlib.h unistd.h sys/stat.h)
AC_CHECK_HEADERS(sys/select.h sys/socket.h sys/time.h sys/types.h sys/wait.h)
+AM_PATH_PYTHON([2],,)
+
AC_PATH_PROG([SED], [sed], [no])
if test "$SED" = "no"; then
AC_MSG_ERROR([The program "sed" is not available. This program is required to build Openbox.])
@@ -259,6 +261,7 @@
obrender/version.h
obt/version.h
version.h
+ data/autostart/openbox-xdg-autostart
])
AC_CONFIG_COMMANDS([doc],
[test -d doc || mkdir doc])
diff -Naur a/data/autostart/autostart.in b/data/autostart/autostart.in
--- a/data/autostart/autostart.in 2014-11-05 09:32:16.000000000 +0100
+++ b/data/autostart/autostart.in 2023-04-07 20:15:20.470780803 +0200
@@ -8,7 +8,7 @@
#
#if test -x @libexecdir@/gnome-settings-daemon >/dev/null; then
# @libexecdir@/gnome-settings-daemon &
-#elif which gnome-settings-daemon >/dev/null; then
+#elif which gnome-settings-daemon >/dev/null 2>&1; then
# gnome-settings-daemon &
#fi
diff -Naur a/data/autostart/openbox-xdg-autostart b/data/autostart/openbox-xdg-autostart
--- a/data/autostart/openbox-xdg-autostart 2013-04-17 14:27:27.000000000 +0200
+++ b/data/autostart/openbox-xdg-autostart 1970-01-01 01:00:00.000000000 +0100
@@ -1,198 +0,0 @@
-#!/usr/bin/env python
-
-# openbox-xdg-autostart runs things based on the XDG autostart specification
-# Copyright (C) 2008 Dana Jansens
-#
-# XDG autostart specification can be found here:
-# http://standards.freedesktop.org/autostart-spec/
-#
-#
-#
-# LICENSE:
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-ME="openbox-xdg-autostart"
-VERSION="1.1"
-
-import os, glob, sys
-try:
- from xdg import BaseDirectory
- from xdg.DesktopEntry import DesktopEntry
- from xdg.Exceptions import ParsingError
-except ImportError:
- print
- print >>sys.stderr, "ERROR:", ME, "requires PyXDG to be installed"
- print
- sys.exit(1)
-
-def main(argv=sys.argv):
- if "--help" in argv[1:]:
- show_help()
- return 0
- if "--version" in argv[1:]:
- show_version()
- return 0
-
- # get the autostart directories
- autodirs = BaseDirectory.load_config_paths("autostart")
-
- # find all the autostart files
- files = []
- for dir in autodirs:
- for path in glob.glob(os.path.join(dir, '*.desktop')):
- try:
- autofile = AutostartFile(path)
- except ParsingError:
- print "Invalid .desktop file: " + path
- else:
- if not autofile in files:
- files.append(autofile)
-
- list = False
- if "--list" in argv[1:]:
- list = True
- argv.remove("--list")
-
- # run them !
- environments = argv[1:]
- for autofile in files:
- if list: autofile.display(environments)
- else: autofile.run(environments)
-
-class AutostartFile:
- def __init__(self, path):
- self.path = path
- self.filename = os.path.basename(path)
- self.dirname = os.path.dirname(path)
- self.de = DesktopEntry(path)
-
- def __eq__(self, other):
- return self.filename == other.filename
-
- def __str__(self):
- return self.path + " : " + self.de.getName()
-
- def _isexecfile(self, path):
- return os.access(path, os.X_OK)
-
- def _findFile(self, path, search, match_func):
- # check empty path
- if not path: return None
- # check absolute path
- if path[0] == '/':
- if match_func(path): return path
- else: return None
- else:
- # check relative path
- for dirname in search.split(os.pathsep):
- if dirname != "":
- candidate = os.path.join(dirname, path)
- if (match_func(candidate)): return candidate
-
- def _alert(self, str, info=False):
- if info:
- print "\t ", str
- else:
- print "\t*", str
-
- def _showInEnvironment(self, envs, verbose=False):
- default = not self.de.getOnlyShowIn()
- noshow = False
- force = False
- for i in self.de.getOnlyShowIn():
- if i in envs: force = True
- for i in self.de.getNotShowIn():
- if i in envs: noshow = True
-
- if verbose:
- if not default and not force:
- s = ""
- for i in self.de.getOnlyShowIn():
- if s: s += ", "
- s += i
- self._alert("Excluded by: OnlyShowIn (" + s + ")")
- if default and noshow and not force:
- s = ""
- for i in self.de.getNotShowIn():
- if s: s += ", "
- s += i
- self._alert("Excluded by: NotShowIn (" + s + ")")
- return (default and not noshow) or force
-
- def _shouldRun(self, envs, verbose=False):
- if not self.de.getExec():
- if verbose: self._alert("Excluded by: Missing Exec field")
- return False
- if self.de.getHidden():
- if verbose: self._alert("Excluded by: Hidden")
- return False
- if self.de.getTryExec():
- if not self._findFile(self.de.getTryExec(), os.getenv("PATH"),
- self._isexecfile):
- if verbose: self._alert("Excluded by: TryExec (" +
- self.de.getTryExec() + ")")
- return False
- if not self._showInEnvironment(envs, verbose):
- return False
- return True
-
- def display(self, envs):
- if self._shouldRun(envs):
- print "[*] " + self.de.getName()
- else:
- print "[ ] " + self.de.getName()
- self._alert("File: " + self.path, info=True)
- if self.de.getExec():
- self._alert("Executes: " + self.de.getExec(), info=True)
- self._shouldRun(envs, True)
- print
-
- def run(self, envs):
- here = os.getcwd()
- if self.de.getPath():
- os.chdir(self.de.getPath())
- if self._shouldRun(envs):
- args = ["/bin/sh", "-c", "exec " + self.de.getExec()]
- os.spawnv(os.P_NOWAIT, args[0], args);
- os.chdir(here)
-
-def show_help():
- print "Usage:", ME, "[OPTION]... [ENVIRONMENT]..."
- print
- print "This tool will run xdg autostart .desktop files"
- print
- print "OPTIONS"
- print " --list Show a list of the files which would be run"
- print " Files which would be run are marked with an asterix"
- print " symbol [*]. For files which would not be run,"
- print " information is given for why they are excluded"
- print " --help Show this help and exit"
- print " --version Show version and copyright information"
- print
- print "ENVIRONMENT specifies a list of environments for which to run autostart"
- print "applications. If none are specified, only applications which do not "
- print "limit themselves to certain environments will be run."
- print
- print "ENVIRONMENT can be one or more of:"
- print " GNOME Gnome Desktop"
- print " KDE KDE Desktop"
- print " ROX ROX Desktop"
- print " XFCE XFCE Desktop"
- print " Old Legacy systems"
- print
-
-def show_version():
- print ME, VERSION
- print "Copyright (c) 2008 Dana Jansens"
- print
-
-if __name__ == "__main__":
- sys.exit(main())
diff -Naur a/data/autostart/openbox-xdg-autostart.in b/data/autostart/openbox-xdg-autostart.in
--- a/data/autostart/openbox-xdg-autostart.in 1970-01-01 01:00:00.000000000 +0100
+++ b/data/autostart/openbox-xdg-autostart.in 2023-04-07 20:15:49.260782004 +0200
@@ -0,0 +1,196 @@
+#!@PYTHON@
+
+# openbox-xdg-autostart runs things based on the XDG autostart specification
+# Copyright (C) 2008 Dana Jansens
+#
+# XDG autostart specification can be found here:
+# http://standards.freedesktop.org/autostart-spec/
+#
+#
+#
+# LICENSE:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+ME="openbox-xdg-autostart"
+VERSION="1.1"
+
+import os, glob, sys
+try:
+ from xdg import BaseDirectory
+ from xdg.DesktopEntry import DesktopEntry
+ from xdg.Exceptions import ParsingError
+except ImportError:
+ sys.stderr.write("\nERROR: %s requires PyXDG to be installed\n" % ME)
+ sys.exit(1)
+
+def main(argv=sys.argv):
+ if "--help" in argv[1:]:
+ show_help()
+ return 0
+ if "--version" in argv[1:]:
+ show_version()
+ return 0
+
+ # get the autostart directories
+ autodirs = BaseDirectory.load_config_paths("autostart")
+
+ # find all the autostart files
+ files = []
+ for dir in autodirs:
+ for path in glob.glob(os.path.join(dir, '*.desktop')):
+ try:
+ autofile = AutostartFile(path)
+ except ParsingError:
+ print("Invalid .desktop file: " + path)
+ else:
+ if not autofile in files:
+ files.append(autofile)
+
+ list = False
+ if "--list" in argv[1:]:
+ list = True
+ argv.remove("--list")
+
+ # run them !
+ environments = argv[1:]
+ for autofile in files:
+ if list: autofile.display(environments)
+ else: autofile.run(environments)
+
+class AutostartFile:
+ def __init__(self, path):
+ self.path = path
+ self.filename = os.path.basename(path)
+ self.dirname = os.path.dirname(path)
+ self.de = DesktopEntry(path)
+
+ def __eq__(self, other):
+ return self.filename == other.filename
+
+ def __str__(self):
+ return self.path + " : " + self.de.getName()
+
+ def _isexecfile(self, path):
+ return os.access(path, os.X_OK)
+
+ def _findFile(self, path, search, match_func):
+ # check empty path
+ if not path: return None
+ # check absolute path
+ if path[0] == '/':
+ if match_func(path): return path
+ else: return None
+ else:
+ # check relative path
+ for dirname in search.split(os.pathsep):
+ if dirname != "":
+ candidate = os.path.join(dirname, path)
+ if (match_func(candidate)): return candidate
+
+ def _alert(self, str, info=False):
+ if info:
+ print("\t ", str)
+ else:
+ print("\t*", str)
+
+ def _showInEnvironment(self, envs, verbose=False):
+ default = not self.de.getOnlyShowIn()
+ noshow = False
+ force = False
+ for i in self.de.getOnlyShowIn():
+ if i in envs: force = True
+ for i in self.de.getNotShowIn():
+ if i in envs: noshow = True
+
+ if verbose:
+ if not default and not force:
+ s = ""
+ for i in self.de.getOnlyShowIn():
+ if s: s += ", "
+ s += i
+ self._alert("Excluded by: OnlyShowIn (" + s + ")")
+ if default and noshow and not force:
+ s = ""
+ for i in self.de.getNotShowIn():
+ if s: s += ", "
+ s += i
+ self._alert("Excluded by: NotShowIn (" + s + ")")
+ return (default and not noshow) or force
+
+ def _shouldRun(self, envs, verbose=False):
+ if not self.de.getExec():
+ if verbose: self._alert("Excluded by: Missing Exec field")
+ return False
+ if self.de.getHidden():
+ if verbose: self._alert("Excluded by: Hidden")
+ return False
+ if self.de.getTryExec():
+ if not self._findFile(self.de.getTryExec(), os.getenv("PATH"),
+ self._isexecfile):
+ if verbose: self._alert("Excluded by: TryExec (" +
+ self.de.getTryExec() + ")")
+ return False
+ if not self._showInEnvironment(envs, verbose):
+ return False
+ return True
+
+ def display(self, envs):
+ if self._shouldRun(envs):
+ print("[*] " + self.de.getName())
+ else:
+ print("[ ] " + self.de.getName())
+ self._alert("File: " + self.path, info=True)
+ if self.de.getExec():
+ self._alert("Executes: " + self.de.getExec(), info=True)
+ self._shouldRun(envs, True)
+ print()
+
+ def run(self, envs):
+ here = os.getcwd()
+ if self.de.getPath():
+ os.chdir(self.de.getPath())
+ if self._shouldRun(envs):
+ args = ["/bin/sh", "-c", "exec " + self.de.getExec()]
+ os.spawnv(os.P_NOWAIT, args[0], args);
+ os.chdir(here)
+
+def show_help():
+ print("Usage:", ME, "[OPTION]... [ENVIRONMENT]...")
+ print()
+ print("This tool will run xdg autostart .desktop files")
+ print()
+ print("OPTIONS")
+ print(" --list Show a list of the files which would be run")
+ print(" Files which would be run are marked with an asterix")
+ print(" symbol [*]. For files which would not be run,")
+ print(" information is given for why they are excluded")
+ print(" --help Show this help and exit")
+ print(" --version Show version and copyright information")
+ print()
+ print("ENVIRONMENT specifies a list of environments for which to run autostart")
+ print("applications. If none are specified, only applications which do not ")
+ print("limit themselves to certain environments will be run.")
+ print()
+ print("ENVIRONMENT can be one or more of:")
+ print(" GNOME Gnome Desktop")
+ print(" KDE KDE Desktop")
+ print(" ROX ROX Desktop")
+ print(" XFCE XFCE Desktop")
+ print(" Old Legacy systems")
+ print()
+
+def show_version():
+ print(ME, VERSION)
+ print("Copyright (c) 2008 Dana Jansens")
+ print()
+
+if __name__ == "__main__":
+ sys.exit(main())
diff -Naur a/data/menu.xml b/data/menu.xml
--- a/data/menu.xml 2009-12-18 19:49:32.000000000 +0100
+++ b/data/menu.xml 2023-04-07 20:13:22.797442560 +0200
@@ -365,6 +365,15 @@
</startupnotify>
</action>
</item>
+ <item label="NuTyX Package Manager">
+ <action name="Execute">
+ <command>sudo flcards</command>
+ <startupnotify>
+ <enable>no</enable>
+ <icon>flcards</icon>
+ </startupnotify>
+ </action>
+ </item>
<separator />
<item label="Reconfigure Openbox">
<action name="Reconfigure" />
diff -Naur a/data/xsession/openbox-kde.desktop.in b/data/xsession/openbox-kde.desktop.in
--- a/data/xsession/openbox-kde.desktop.in 2013-04-17 14:27:27.000000000 +0200
+++ b/data/xsession/openbox-kde.desktop.in 2023-04-07 20:15:40.267448295 +0200
@@ -2,6 +2,6 @@
Name=KDE/Openbox
Comment=Use the Openbox window manager inside of the K Desktop Environment
Exec=@bindir@/openbox-kde-session
-TryExec=startkde
+TryExec=/usr/bin/startplasma-x11
Icon=openbox
Type=Application
diff -Naur a/data/xsession/openbox-kde-session.in b/data/xsession/openbox-kde-session.in
--- a/data/xsession/openbox-kde-session.in 2011-10-16 18:13:18.000000000 +0200
+++ b/data/xsession/openbox-kde-session.in 2023-04-07 20:15:40.267448295 +0200
@@ -17,4 +17,4 @@
# Run KDE with Openbox as its window manager
export KDEWM="@bindir@/openbox"
-exec startkde "$@"
+exec /usr/bin/startplasma-x11 "$@"
diff -Naur a/obrender/theme.c b/obrender/theme.c
--- a/obrender/theme.c 2014-11-06 04:22:49.000000000 +0100
+++ b/obrender/theme.c 2023-04-07 20:16:05.560782684 +0200
@@ -1494,8 +1494,10 @@
READ_BUTTON_MASK_COPY(disabled, btn->unpressed_mask);
READ_BUTTON_MASK_COPY(hover, btn->unpressed_mask);
if (toggled_mask) {
- READ_BUTTON_MASK_COPY(pressed_toggled, btn->unpressed_toggled_mask);
- READ_BUTTON_MASK_COPY(hover_toggled, btn->unpressed_toggled_mask);
+ g_snprintf(name, 128, "%s_toggled_pressed.xbm", btnname);
+ READ_MASK_COPY(name, btn->pressed_toggled_mask, btn->unpressed_toggled_mask);
+ g_snprintf(name, 128, "%s_toggled_hover.xbm", btnname);
+ READ_MASK_COPY(name, btn->hover_toggled_mask, btn->unpressed_toggled_mask);
}
#define READ_BUTTON_APPEARANCE(typedots, type, fallback) \
@@ -1532,8 +1534,8 @@
READ_BUTTON_APPEARANCE("disabled", disabled, 0);
READ_BUTTON_APPEARANCE("hover", hover, 0);
if (toggled_mask) {
- READ_BUTTON_APPEARANCE("unpressed.toggled", unpressed_toggled, 1);
- READ_BUTTON_APPEARANCE("pressed.toggled", pressed_toggled, 0);
- READ_BUTTON_APPEARANCE("hover.toggled", hover_toggled, 0);
+ READ_BUTTON_APPEARANCE("toggled.unpressed", unpressed_toggled, 1);
+ READ_BUTTON_APPEARANCE("toggled.pressed", pressed_toggled, 0);
+ READ_BUTTON_APPEARANCE("toggled.hover", hover_toggled, 0);
}
}
diff -Naur a/openbox/client.c b/openbox/client.c
--- a/openbox/client.c 2015-03-22 14:08:16.000000000 +0100
+++ b/openbox/client.c 2023-04-07 20:17:39.684119944 +0200
@@ -941,7 +941,7 @@
!g_pattern_match(app->role,
strlen(self->role), self->role, NULL))
match = FALSE;
- else if (app->title &&
+ else if (app->title && self->title &&
!g_pattern_match(app->title,
strlen(self->title), self->title, NULL))
match = FALSE;
@@ -2702,9 +2702,12 @@
void client_calc_layer(ObClient *self)
{
GList *it;
+ /* the client_calc_layer_internal calls below modify stacking_list,
+ so we have to make a copy to iterate over */
+ GList *list = g_list_copy(stacking_list);
/* skip over stuff above fullscreen layer */
- for (it = stacking_list; it; it = g_list_next(it))
+ for (it = list; it; it = g_list_next(it))
if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
/* find the windows in the fullscreen layer, and mark them not-visited */
@@ -2717,7 +2720,7 @@
client_calc_layer_internal(self);
/* skip over stuff above fullscreen layer */
- for (it = stacking_list; it; it = g_list_next(it))
+ for (it = list; it; it = g_list_next(it))
if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
/* now recalc any windows in the fullscreen layer which have not
@@ -2728,6 +2731,8 @@
!WINDOW_AS_CLIENT(it->data)->visited)
client_calc_layer_internal(it->data);
}
+
+ g_list_free(it);
}
gboolean client_should_show(ObClient *self)
diff -Naur a/openbox/client.c.orig b/openbox/client.c.orig
--- a/openbox/client.c.orig 1970-01-01 01:00:00.000000000 +0100
+++ b/openbox/client.c.orig 2023-04-07 20:15:10.274113710 +0200
@@ -0,0 +1,4722 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "client.h"
+#include "debug.h"
+#include "startupnotify.h"
+#include "dock.h"
+#include "screen.h"
+#include "moveresize.h"
+#include "ping.h"
+#include "place.h"
+#include "frame.h"
+#include "session.h"
+#include "event.h"
+#include "grab.h"
+#include "prompt.h"
+#include "focus.h"
+#include "focus_cycle.h"
+#include "stacking.h"
+#include "openbox.h"
+#include "group.h"
+#include "config.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "obrender/render.h"
+#include "gettext.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+# include <signal.h> /* for kill() */
+#endif
+
+#include <glib.h>
+#include <X11/Xutil.h>
+
+/*! The event mask to grab on client windows */
+#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
+ ColormapChangeMask)
+
+#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
+ ButtonMotionMask)
+
+typedef struct
+{
+ ObClientCallback func;
+ gpointer data;
+} ClientCallback;
+
+GList *client_list = NULL;
+
+static GSList *client_destroy_notifies = NULL;
+static RrImage *client_default_icon = NULL;
+
+static void client_get_all(ObClient *self, gboolean real);
+static void client_get_startup_id(ObClient *self);
+static void client_get_session_ids(ObClient *self);
+static void client_save_app_rule_values(ObClient *self);
+static void client_get_area(ObClient *self);
+static void client_get_desktop(ObClient *self);
+static void client_get_state(ObClient *self);
+static void client_get_shaped(ObClient *self);
+static void client_get_colormap(ObClient *self);
+static void client_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise);
+static void client_change_allowed_actions(ObClient *self);
+static void client_change_state(ObClient *self);
+static void client_change_wm_state(ObClient *self);
+static void client_apply_startup_state(ObClient *self,
+ gint x, gint y, gint w, gint h);
+static void client_restore_session_state(ObClient *self);
+static gboolean client_restore_session_stacking(ObClient *self);
+static ObAppSettings *client_get_settings_state(ObClient *self);
+static void client_update_transient_tree(ObClient *self,
+ ObGroup *oldgroup, ObGroup *newgroup,
+ gboolean oldgtran, gboolean newgtran,
+ ObClient* oldparent,
+ ObClient *newparent);
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade);
+static GSList *client_search_all_top_parents_internal(ObClient *self,
+ gboolean bylayer,
+ ObStackingLayer layer);
+static void client_call_notifies(ObClient *self, GSList *list);
+static void client_ping_event(ObClient *self, gboolean dead);
+static void client_prompt_kill(ObClient *self);
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ gboolean request_from_user,
+ Time steal_time, Time launch_time);
+static void client_setup_default_decor_and_functions(ObClient *self);
+static void client_setup_decor_undecorated(ObClient *self);
+
+void client_startup(gboolean reconfig)
+{
+ client_default_icon = RrImageNewFromData(
+ ob_rr_icons, ob_rr_theme->def_win_icon,
+ ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
+
+ if (reconfig) return;
+
+ client_set_list();
+}
+
+void client_shutdown(gboolean reconfig)
+{
+ RrImageUnref(client_default_icon);
+ client_default_icon = NULL;
+
+ if (reconfig) return;
+}
+
+static void client_call_notifies(ObClient *self, GSList *list)
+{
+ GSList *it;
+
+ for (it = list; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ d->func(self, d->data);
+ }
+}
+
+void client_add_destroy_notify(ObClientCallback func, gpointer data)
+{
+ ClientCallback *d = g_slice_new(ClientCallback);
+ d->func = func;
+ d->data = data;
+ client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
+}
+
+void client_remove_destroy_notify(ObClientCallback func)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func) {
+ g_slice_free(ClientCallback, d);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
+ break;
+ }
+ }
+}
+
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func && d->data == data) {
+ g_slice_free(ClientCallback, d);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
+ break;
+ }
+ }
+}
+
+void client_set_list(void)
+{
+ Window *windows, *win_it;
+ GList *it;
+ guint size = g_list_length(client_list);
+
+ /* create an array of the window ids */
+ if (size > 0) {
+ windows = g_new(Window, size);
+ win_it = windows;
+ for (it = client_list; it; it = g_list_next(it), ++win_it)
+ *win_it = ((ObClient*)it->data)->window;
+ } else
+ windows = NULL;
+
+ OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
+ (gulong*)windows, size);
+
+ if (windows)
+ g_free(windows);
+
+ stacking_set_list();
+}
+
+void client_manage(Window window, ObPrompt *prompt)
+{
+ ObClient *self;
+ XSetWindowAttributes attrib_set;
+ gboolean try_activate = FALSE;
+ gboolean do_activate;
+ ObAppSettings *settings;
+ gboolean transient = FALSE;
+ Rect place;
+ Time launch_time;
+ guint32 user_time;
+ gboolean obplaced;
+ gulong ignore_start = FALSE;
+
+ ob_debug("Managing window: 0x%lx", window);
+
+ /* choose the events we want to receive on the CLIENT window
+ (ObPrompt windows can request events too) */
+ attrib_set.event_mask = CLIENT_EVENTMASK |
+ (prompt ? prompt->event_mask : 0);
+ attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
+ XChangeWindowAttributes(obt_display, window,
+ CWEventMask|CWDontPropagate, &attrib_set);
+
+ /* create the ObClient struct, and populate it from the hints on the
+ window */
+ self = g_slice_new0(ObClient);
+ self->obwin.type = OB_WINDOW_CLASS_CLIENT;
+ self->window = window;
+ self->prompt = prompt;
+ self->managed = TRUE;
+
+ /* non-zero defaults */
+ self->wmstate = WithdrawnState; /* make sure it gets updated first time */
+ self->gravity = NorthWestGravity;
+ self->desktop = screen_num_desktops; /* always an invalid value */
+
+ /* get all the stuff off the window */
+ client_get_all(self, TRUE);
+
+ ob_debug("Window type: %d", self->type);
+ ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
+ ob_debug("Window name: %s class: %s role: %s title: %s",
+ self->name, self->class, self->role, self->title);
+ ob_debug("Window group name: %s group class: %s",
+ self->group_name, self->group_class);
+
+ /* per-app settings override stuff from client_get_all, and return the
+ settings for other uses too. the returned settings is a shallow copy,
+ that needs to be freed with g_free(). */
+ settings = client_get_settings_state(self);
+
+ /* the session should get the last say though */
+ client_restore_session_state(self);
+
+ /* the per-app settings/session may have changed the decorations for
+ the window, so we setup decorations for that here. this is a special
+ case because we want to place the window according to these decoration
+ changes.
+ we do this before setting up the frame so that it will reflect the
+ decorations of the window as it will be placed on screen.
+ */
+ client_setup_decor_undecorated(self);
+
+ /* specify that if we exit, the window should not be destroyed and
+ should be reparented back to root automatically, unless we are managing
+ an internal ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(obt_display, window, SetModeInsert);
+
+ /* create the decoration frame for the client window */
+ self->frame = frame_new(self);
+
+ frame_grab_client(self->frame);
+
+ /* we've grabbed everything and set everything that we need to at mapping
+ time now */
+ grab_server(FALSE);
+
+ /* this needs to occur once we have a frame, since it sets a property on
+ the frame */
+ client_update_opacity(self);
+
+ /* don't put helper/modal windows on a different desktop if they are
+ related to the focused window. */
+ if (!screen_compare_desktops(self->desktop, screen_desktop) &&
+ focus_client && client_search_transient(focus_client, self) &&
+ (client_helper(self) || self->modal))
+ {
+ self->desktop = screen_desktop;
+ }
+
+ /* tell startup notification that this app started */
+ launch_time = sn_app_started(self->startup_id, self->class, self->name);
+
+ if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
+ user_time = event_time();
+
+ /* do this after we have a frame.. it uses the frame to help determine the
+ WM_STATE to apply. */
+ client_change_state(self);
+
+ /* add ourselves to the focus order */
+ focus_order_add_new(self);
+
+ /* do this to add ourselves to the stacking list in a non-intrusive way */
+ client_calc_layer(self);
+
+ /* focus the new window? */
+ if (ob_state() != OB_STATE_STARTING &&
+ (!self->session || self->session->focused) &&
+ /* this means focus=true for window is same as config_focus_new=true */
+ ((config_focus_new || settings->focus == 1) ||
+ client_search_focus_tree_full(self)) &&
+ /* NET_WM_USER_TIME 0 when mapping means don't focus */
+ (user_time != 0) &&
+ /* this checks for focus=false for the window */
+ settings->focus != 0 &&
+ focus_valid_target(self, self->desktop,
+ FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
+ settings->focus == 1))
+ {
+ try_activate = TRUE;
+ }
+
+ /* remove the client's border */
+ XSetWindowBorderWidth(obt_display, self->window, 0);
+
+ /* adjust the frame to the client's size before showing or placing
+ the window */
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+ frame_adjust_client_area(self->frame);
+
+ /* where the frame was placed is where the window was originally */
+ place = self->area;
+
+ ob_debug("Going to try activate new window? %s",
+ try_activate ? "yes" : "no");
+ if (try_activate)
+ do_activate = client_can_steal_focus(
+ self, settings->focus == 1,
+ (!!launch_time || settings->focus == 1),
+ event_time(), launch_time);
+ else
+ do_activate = FALSE;
+
+ /* figure out placement for the window if the window is new */
+ if (ob_state() == OB_STATE_RUNNING) {
+ ob_debug("Positioned: %s @ %d %d",
+ (!self->positioned ? "no" :
+ (self->positioned == PPosition ? "program specified" :
+ (self->positioned == USPosition ? "user specified" :
+ (self->positioned == (PPosition | USPosition) ?
+ "program + user specified" :
+ "BADNESS !?")))), place.x, place.y);
+
+ ob_debug("Sized: %s @ %d %d",
+ (!self->sized ? "no" :
+ (self->sized == PSize ? "program specified" :
+ (self->sized == USSize ? "user specified" :
+ (self->sized == (PSize | USSize) ?
+ "program + user specified" :
+ "BADNESS !?")))), place.width, place.height);
+
+ obplaced = place_client(self, do_activate, &place, settings);
+
+ /* watch for buggy apps that ask to be placed at (0,0) when there is
+ a strut there */
+ if (!obplaced && place.x == 0 && place.y == 0 &&
+ /* non-normal windows are allowed */
+ client_normal(self) &&
+ /* oldschool fullscreen windows are allowed */
+ !client_is_oldfullscreen(self, &place))
+ {
+ Rect *r;
+
+ r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
+ if (r->x || r->y) {
+ place.x = r->x;
+ place.y = r->y;
+ ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
+ }
+ g_slice_free(Rect, r);
+ }
+
+ /* make sure the window is visible. */
+ client_find_onscreen(self, &place.x, &place.y,
+ place.width, place.height,
+ /* non-normal clients has less rules, and
+ windows that are being restored from a
+ session do also. we can assume you want
+ it back where you saved it. Clients saying
+ they placed themselves are subjected to
+ harder rules, ones that are placed by
+ place.c or by the user are allowed partially
+ off-screen and on xinerama divides (ie,
+ it is up to the placement routines to avoid
+ the xinerama divides)
+
+ children and splash screens are forced on
+ screen, but i don't remember why i decided to
+ do that.
+ */
+ ob_state() == OB_STATE_RUNNING &&
+ (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_SPLASH ||
+ (!((self->positioned & USPosition) ||
+ settings->pos_given) &&
+ client_normal(self) &&
+ !self->session &&
+ /* don't move oldschool fullscreen windows to
+ fit inside the struts (fixes Acroread, which
+ makes its fullscreen window fit the screen
+ but it is not USSize'd or USPosition'd) */
+ !client_is_oldfullscreen(self, &place))));
+ }
+
+ /* if the window isn't user-sized, then make it fit inside
+ the visible screen area on its monitor. Use basically the same rules
+ for forcing the window on screen in the client_find_onscreen call.
+
+ do this after place_client, it chooses the monitor!
+
+ splash screens get "transient" set to TRUE by
+ the place_client call
+ */
+ if (ob_state() == OB_STATE_RUNNING &&
+ (transient ||
+ (!(self->sized & USSize || self->positioned & USPosition) &&
+ client_normal(self) &&
+ !self->session &&
+ /* don't shrink oldschool fullscreen windows to fit inside the
+ struts (fixes Acroread, which makes its fullscreen window
+ fit the screen but it is not USSize'd or USPosition'd) */
+ !client_is_oldfullscreen(self, &place))))
+ {
+ Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
+
+ /* get the size of the frame */
+ place.width += self->frame->size.left + self->frame->size.right;
+ place.height += self->frame->size.top + self->frame->size.bottom;
+
+ /* fit the window inside the area */
+ place.width = MIN(place.width, a->width);
+ place.height = MIN(place.height, a->height);
+
+ ob_debug("setting window size to %dx%d", place.width, place.height);
+
+ /* get the size of the client back */
+ place.width -= self->frame->size.left + self->frame->size.right;
+ place.height -= self->frame->size.top + self->frame->size.bottom;
+
+ g_slice_free(Rect, a);
+ }
+
+ ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
+ "some restrictions may apply",
+ self->window, place.x, place.y, place.width, place.height);
+ if (self->session)
+ ob_debug(" but session requested %d, %d %d x %d instead, "
+ "overriding",
+ self->session->x, self->session->y,
+ self->session->w, self->session->h);
+
+ /* do this after the window is placed, so the premax/prefullscreen numbers
+ won't be all wacko!!
+
+ this also places the window
+ */
+ client_apply_startup_state(self, place.x, place.y,
+ place.width, place.height);
+
+ /* set the initial value of the desktop hint, when one wasn't requested
+ on map. */
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
+ /* grab mouse bindings before showing the window */
+ mouse_grab_for_client(self, TRUE);
+
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ /* this has to happen before we try focus the window, but we want it to
+ happen after the client's stacking has been determined or it looks bad
+ */
+ client_show(self);
+
+ /* activate/hilight/raise the window */
+ if (try_activate) {
+ if (do_activate) {
+ gboolean stacked = client_restore_session_stacking(self);
+ client_present(self, FALSE, !stacked, TRUE);
+ }
+ else {
+ /* if the client isn't stealing focus, then hilite it so the user
+ knows it is there, but don't do this if we're restoring from a
+ session */
+ if (!client_restore_session_stacking(self))
+ client_hilite(self, TRUE);
+ }
+ }
+ else {
+ /* This may look rather odd. Well it's because new windows are added
+ to the stacking order non-intrusively. If we're not going to focus
+ the new window or hilite it, then we raise it to the top. This will
+ take affect for things that don't get focused like splash screens.
+ Also if you don't have focus_new enabled, then it's going to get
+ raised to the top. Legacy begets legacy I guess?
+ */
+ if (!client_restore_session_stacking(self))
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ }
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ /* add to client list/map */
+ client_list = g_list_append(client_list, self);
+ window_add(&self->window, CLIENT_AS_WINDOW(self));
+
+ /* this has to happen after we're in the client_list */
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+
+ /* update the list hints */
+ client_set_list();
+
+ /* free the ObAppSettings shallow copy */
+ g_slice_free(ObAppSettings, settings);
+
+ ob_debug("Managed window 0x%lx plate 0x%x (%s)",
+ window, self->frame->window, self->class);
+}
+
+ObClient *client_fake_manage(Window window)
+{
+ ObClient *self;
+ ObAppSettings *settings;
+
+ ob_debug("Pretend-managing window: %lx", window);
+
+ /* do this minimal stuff to figure out the client's decorations */
+
+ self = g_slice_new0(ObClient);
+ self->window = window;
+
+ client_get_all(self, FALSE);
+ /* per-app settings override stuff, and return the settings for other
+ uses too. this returns a shallow copy that needs to be freed */
+ settings = client_get_settings_state(self);
+
+ /* create the decoration frame for the client window and adjust its size */
+ self->frame = frame_new(self);
+
+ client_apply_startup_state(self, self->area.x, self->area.y,
+ self->area.width, self->area.height);
+
+ ob_debug("gave extents left %d right %d top %d bottom %d",
+ self->frame->size.left, self->frame->size.right,
+ self->frame->size.top, self->frame->size.bottom);
+
+ /* free the ObAppSettings shallow copy */
+ g_slice_free(ObAppSettings, settings);
+
+ return self;
+}
+
+void client_unmanage_all(void)
+{
+ while (client_list)
+ client_unmanage(client_list->data);
+}
+
+void client_unmanage(ObClient *self)
+{
+ GSList *it;
+ gulong ignore_start;
+
+ ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
+ self->window, self->frame->window,
+ self->class, self->title ? self->title : "");
+
+ g_assert(self != NULL);
+
+ /* we dont want events no more. do this before hiding the frame so we
+ don't generate more events */
+ XSelectInput(obt_display, self->window, NoEventMask);
+
+ /* ignore enter events from the unmap so it doesnt mess with the focus */
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ frame_hide(self->frame);
+ /* flush to send the hide to the server quickly */
+ XFlush(obt_display);
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ mouse_grab_for_client(self, FALSE);
+
+ self->managed = FALSE;
+
+ /* remove the window from our save set, unless we are managing an internal
+ ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(obt_display, self->window, SetModeDelete);
+
+ /* update the focus lists */
+ focus_order_remove(self);
+ if (client_focused(self)) {
+ /* don't leave an invalid focus_client */
+ focus_client = NULL;
+ }
+
+ /* if we're prompting to kill the client, close that */
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+
+ client_list = g_list_remove(client_list, self);
+ stacking_remove(self);
+ window_remove(self->window);
+
+ /* once the client is out of the list, update the struts to remove its
+ influence */
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+
+ client_call_notifies(self, client_destroy_notifies);
+
+ /* tell our parent(s) that we're gone */
+ for (it = self->parents; it; it = g_slist_next(it))
+ ((ObClient*)it->data)->transients =
+ g_slist_remove(((ObClient*)it->data)->transients,self);
+
+ /* tell our transients that we're gone */
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ ((ObClient*)it->data)->parents =
+ g_slist_remove(((ObClient*)it->data)->parents, self);
+ /* we could be keeping our children in a higher layer */
+ client_calc_layer(it->data);
+ }
+
+ /* remove from its group */
+ if (self->group) {
+ group_remove(self->group, self);
+ self->group = NULL;
+ }
+
+ /* restore the window's original geometry so it is not lost */
+ {
+ Rect a;
+
+ a = self->area;
+
+ if (self->fullscreen)
+ a = self->pre_fullscreen_area;
+ else if (self->max_horz || self->max_vert) {
+ if (self->max_horz) {
+ a.x = self->pre_max_area.x;
+ a.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ a.y = self->pre_max_area.y;
+ a.height = self->pre_max_area.height;
+ }
+ }
+
+ self->fullscreen = self->max_horz = self->max_vert = FALSE;
+ /* let it be moved and resized no matter what */
+ self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
+ self->decorations = 0; /* unmanaged windows have no decor */
+
+ /* give the client its border back */
+ XSetWindowBorderWidth(obt_display, self->window, self->border_width);
+
+ client_move_resize(self, a.x, a.y, a.width, a.height);
+ }
+
+ /* reparent the window out of the frame, and free the frame */
+ frame_release_client(self->frame);
+ frame_free(self->frame);
+ self->frame = NULL;
+
+ if (ob_state() != OB_STATE_EXITING) {
+ /* these values should not be persisted across a window
+ unmapping/mapping */
+ OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
+ OBT_PROP_ERASE(self->window, NET_WM_STATE);
+ OBT_PROP_ERASE(self->window, WM_STATE);
+ } else {
+ /* if we're left in an unmapped state, the client wont be mapped.
+ this is bad, since we will no longer be managing the window on
+ restart */
+ XMapWindow(obt_display, self->window);
+ }
+
+ /* these should not be left on the window ever. other window managers
+ don't necessarily use them and it will mess them up (like compiz) */
+ OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
+ OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
+
+ /* update the list hints */
+ client_set_list();
+
+ ob_debug("Unmanaged window 0x%lx", self->window);
+
+ /* free all data allocated in the client struct */
+ RrImageUnref(self->icon_set);
+ g_slist_free(self->transients);
+ g_free(self->startup_id);
+ g_free(self->wm_command);
+ g_free(self->title);
+ g_free(self->icon_title);
+ g_free(self->original_title);
+ g_free(self->name);
+ g_free(self->class);
+ g_free(self->role);
+ g_free(self->group_name);
+ g_free(self->group_class);
+ g_free(self->client_machine);
+ g_free(self->sm_client_id);
+ g_slice_free(ObClient, self);
+}
+
+void client_fake_unmanage(ObClient *self)
+{
+ /* this is all that got allocated to get the decorations */
+
+ frame_free(self->frame);
+ g_slice_free(ObClient, self);
+}
+
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ gboolean request_from_user,
+ Time steal_time,
+ Time launch_time)
+{
+ gboolean steal;
+ gboolean relative_focused;
+
+ steal = TRUE;
+
+ relative_focused = (focus_client != NULL &&
+ (client_search_focus_tree_full(self) != NULL ||
+ client_search_focus_group_full(self) != NULL));
+
+ /* This is focus stealing prevention */
+ ob_debug("Want to focus window 0x%x at time %u "
+ "launched at %u (last user interaction time %u) "
+ "request from %s, allow other desktop: %s, "
+ "desktop switch time %u",
+ self->window, steal_time, launch_time,
+ event_last_user_time,
+ (request_from_user ? "user" : "other"),
+ (allow_other_desktop ? "yes" : "no"),
+ screen_desktop_user_time);
+
+ /*
+ if no launch time is provided for an application, make one up.
+
+ if the window is related to other existing windows
+ and one of those windows was the last used
+ then we will give it a launch time equal to the last user time,
+ which will end up giving the window focus probably.
+ else
+ the window is related to other windows, but you are not working in
+ them?
+ seems suspicious, so we will give it a launch time of
+ NOW - STEAL_INTERVAL,
+ so it will be given focus only if we didn't use something else
+ during the steal interval.
+ else
+ the window is all on its own, so we can't judge it. give it a launch
+ time equal to the last user time, so it will probably take focus.
+
+ this way running things from a terminal will give them focus, but popups
+ without a launch time shouldn't steal focus so easily.
+ */
+
+ if (!launch_time) {
+ if (client_has_relative(self)) {
+ if (event_last_user_time && client_search_focus_group_full(self)) {
+ /* our relative is focused */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - window in active "
+ "group", launch_time);
+ }
+ else if (!request_from_user) {
+ /* has relatives which are not being used. suspicious */
+ launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
+ ob_debug("Unknown launch time, using %u - window in inactive "
+ "group", launch_time);
+ }
+ else {
+ /* has relatives which are not being used, but the user seems
+ to want to go there! */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - user request",
+ launch_time);
+ }
+ }
+ else {
+ /* the window is on its own, probably the user knows it is going
+ to appear */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - independent window",
+ launch_time);
+ }
+ }
+
+ /* if it's on another desktop
+ and if allow_other_desktop is true, we generally let it steal focus.
+ but if it didn't come from the user, don't let it steal unless it was
+ launched before the user switched desktops.
+ focus, unless it was launched after we changed desktops and the request
+ came from the user
+ */
+ if (!screen_compare_desktops(screen_desktop, self->desktop)) {
+ /* must be allowed */
+ if (!allow_other_desktop) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because its on another desktop");
+ }
+ /* if we don't know when the desktop changed, but request is from an
+ application, don't let it change desktop on you */
+ else if (!request_from_user) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because non-user request");
+ }
+ }
+ /* If something is focused... */
+ else if (focus_client) {
+ /* If the user is working in another window right now, then don't
+ steal focus */
+ if (!relative_focused &&
+ event_last_user_time &&
+ /* last user time must be strictly > launch_time to block focus */
+ (event_time_after(event_last_user_time, launch_time) &&
+ event_last_user_time != launch_time) &&
+ event_time_after(event_last_user_time,
+ steal_time - OB_EVENT_USER_TIME_DELAY))
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because the user is "
+ "working in another window that is not its relative");
+ }
+ /* Don't move focus if it's not going to go to this window
+ anyway */
+ else if (client_focus_target(self) != self) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because another window "
+ "would get the focus anyway");
+ }
+ /* For requests that don't come from the user */
+ else if (!request_from_user) {
+ /* If the new window is a transient (and its relatives aren't
+ focused) */
+ if (client_has_parent(self) && !relative_focused) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is a "
+ "transient, and its relatives aren't focused");
+ }
+ /* Don't steal focus from globally active clients.
+ I stole this idea from KWin. It seems nice.
+ */
+ else if (!(focus_client->can_focus || focus_client->focus_notify))
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because a globally "
+ "active client has focus");
+ }
+ /* Don't move focus if the window is not visible on the current
+ desktop and none of its relatives are focused */
+ else if (!allow_other_desktop &&
+ !screen_compare_desktops(self->desktop, screen_desktop) &&
+ !relative_focused)
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
+ }
+ }
+
+ if (!steal)
+ ob_debug("Focus stealing prevention activated for %s at "
+ "time %u (last user interaction time %u)",
+ self->title, steal_time, event_last_user_time);
+ else
+ ob_debug("Allowing focus stealing for %s at time %u (last user "
+ "interaction time %u)",
+ self->title, steal_time, event_last_user_time);
+ return steal;
+}
+
+/*! Returns a new structure containing the per-app settings for this client.
+ The returned structure needs to be freed with g_free. */
+static ObAppSettings *client_get_settings_state(ObClient *self)
+{
+ ObAppSettings *settings;
+ GSList *it;
+
+ settings = config_create_app_settings();
+
+ for (it = config_per_app_settings; it; it = g_slist_next(it)) {
+ ObAppSettings *app = it->data;
+ gboolean match = TRUE;
+
+ g_assert(app->name != NULL || app->class != NULL ||
+ app->role != NULL || app->title != NULL ||
+ app->group_name != NULL || app->group_class != NULL ||
+ (signed)app->type >= 0);
+
+ if (app->name &&
+ !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
+ match = FALSE;
+ else if (app->group_name &&
+ !g_pattern_match(app->group_name,
+ strlen(self->group_name), self->group_name, NULL))
+ match = FALSE;
+ else if (app->class &&
+ !g_pattern_match(app->class,
+ strlen(self->class), self->class, NULL))
+ match = FALSE;
+ else if (app->group_class &&
+ !g_pattern_match(app->group_class,
+ strlen(self->group_class), self->group_class,
+ NULL))
+ match = FALSE;
+ else if (app->role &&
+ !g_pattern_match(app->role,
+ strlen(self->role), self->role, NULL))
+ match = FALSE;
+ else if (app->title && self->title &&
+ !g_pattern_match(app->title,
+ strlen(self->title), self->title, NULL))
+ match = FALSE;
+ else if ((signed)app->type >= 0 && app->type != self->type) {
+ match = FALSE;
+ }
+
+ if (match) {
+ ob_debug("Window matching: %s", app->name);
+
+ /* copy the settings to our struct, overriding the existing
+ settings if they are not defaults */
+ config_app_settings_copy_non_defaults(app, settings);
+ }
+ }
+
+ if (settings->shade != -1)
+ self->shaded = !!settings->shade;
+ if (settings->decor != -1)
+ self->undecorated = !settings->decor;
+ if (settings->iconic != -1)
+ self->iconic = !!settings->iconic;
+ if (settings->skip_pager != -1)
+ self->skip_pager = !!settings->skip_pager;
+ if (settings->skip_taskbar != -1)
+ self->skip_taskbar = !!settings->skip_taskbar;
+
+ if (settings->max_vert != -1)
+ self->max_vert = !!settings->max_vert;
+ if (settings->max_horz != -1)
+ self->max_horz = !!settings->max_horz;
+
+ if (settings->fullscreen != -1)
+ self->fullscreen = !!settings->fullscreen;
+
+ if (settings->desktop) {
+ if (settings->desktop == DESKTOP_ALL)
+ self->desktop = settings->desktop;
+ else if (settings->desktop > 0 &&
+ settings->desktop <= screen_num_desktops)
+ self->desktop = settings->desktop - 1;
+ }
+
+ if (settings->layer == -1) {
+ self->below = TRUE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 0) {
+ self->below = FALSE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 1) {
+ self->below = FALSE;
+ self->above = TRUE;
+ }
+ return settings;
+}
+
+static void client_restore_session_state(ObClient *self)
+{
+ GList *it;
+
+ ob_debug_type(OB_DEBUG_SM,
+ "Restore session for client %s", self->title);
+
+ if (!(it = session_state_find(self))) {
+ ob_debug_type(OB_DEBUG_SM,
+ "Session data not found for client %s", self->title);
+ return;
+ }
+
+ self->session = it->data;
+
+ ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
+ self->title);
+
+ RECT_SET_POINT(self->area, self->session->x, self->session->y);
+ self->positioned = USPosition;
+ self->sized = USSize;
+ if (self->session->w > 0)
+ self->area.width = self->session->w;
+ if (self->session->h > 0)
+ self->area.height = self->session->h;
+ XResizeWindow(obt_display, self->window,
+ self->area.width, self->area.height);
+
+ self->desktop = (self->session->desktop == DESKTOP_ALL ?
+ self->session->desktop :
+ MIN(screen_num_desktops - 1, self->session->desktop));
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
+ self->shaded = self->session->shaded;
+ self->iconic = self->session->iconic;
+ self->skip_pager = self->session->skip_pager;
+ self->skip_taskbar = self->session->skip_taskbar;
+ self->fullscreen = self->session->fullscreen;
+ self->above = self->session->above;
+ self->below = self->session->below;
+ self->max_horz = self->session->max_horz;
+ self->max_vert = self->session->max_vert;
+ self->undecorated = self->session->undecorated;
+}
+
+static gboolean client_restore_session_stacking(ObClient *self)
+{
+ GList *it, *mypos;
+
+ if (!self->session) return FALSE;
+
+ mypos = g_list_find(session_saved_state, self->session);
+ if (!mypos) return FALSE;
+
+ /* start above me and look for the first client */
+ for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
+ GList *cit;
+
+ for (cit = client_list; cit; cit = g_list_next(cit)) {
+ ObClient *c = cit->data;
+ /* found a client that was in the session, so go below it */
+ if (c->session == it->data) {
+ stacking_below(CLIENT_AS_WINDOW(self),
+ CLIENT_AS_WINDOW(cit->data));
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void client_move_onscreen(ObClient *self, gboolean rude)
+{
+ gint x = self->area.x;
+ gint y = self->area.y;
+ if (client_find_onscreen(self, &x, &y,
+ self->area.width,
+ self->area.height, rude)) {
+ client_move(self, x, y);
+ }
+}
+
+gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
+ gboolean rude)
+{
+ gint ox = *x, oy = *y;
+ gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
+ gint fw, fh;
+ Rect desired;
+ guint i;
+ gboolean found_mon;
+
+ RECT_SET(desired, *x, *y, w, h);
+ frame_rect_to_frame(self->frame, &desired);
+
+ /* get where the frame would be */
+ frame_client_gravity(self->frame, x, y);
+
+ /* get the requested size of the window with decorations */
+ fw = self->frame->size.left + w + self->frame->size.right;
+ fh = self->frame->size.top + h + self->frame->size.bottom;
+
+ /* If rudeness wasn't requested, then still be rude in a given direction
+ if the client is not moving, only resizing in that direction */
+ if (!rude) {
+ Point oldtl, oldtr, oldbl, oldbr;
+ Point newtl, newtr, newbl, newbr;
+ gboolean stationary_l, stationary_r, stationary_t, stationary_b;
+
+ POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
+ POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
+ self->frame->area.y + self->frame->area.height - 1);
+ POINT_SET(oldtr, oldbr.x, oldtl.y);
+ POINT_SET(oldbl, oldtl.x, oldbr.y);
+
+ POINT_SET(newtl, *x, *y);
+ POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
+ POINT_SET(newtr, newbr.x, newtl.y);
+ POINT_SET(newbl, newtl.x, newbr.y);
+
+ /* is it moving or just resizing from some corner? */
+ stationary_l = oldtl.x == newtl.x;
+ stationary_r = oldtr.x == newtr.x;
+ stationary_t = oldtl.y == newtl.y;
+ stationary_b = oldbl.y == newbl.y;
+
+ /* if left edge is growing and didnt move right edge */
+ if (stationary_r && newtl.x < oldtl.x)
+ rudel = TRUE;
+ /* if right edge is growing and didnt move left edge */
+ if (stationary_l && newtr.x > oldtr.x)
+ ruder = TRUE;
+ /* if top edge is growing and didnt move bottom edge */
+ if (stationary_b && newtl.y < oldtl.y)
+ rudet = TRUE;
+ /* if bottom edge is growing and didnt move top edge */
+ if (stationary_t && newbl.y > oldbl.y)
+ rudeb = TRUE;
+ }
+
+ /* we iterate through every monitor that the window is at least partially
+ on, to make sure it is obeying the rules on them all
+
+ if the window does not appear on any monitors, then use the first one
+ */
+ found_mon = FALSE;
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *a;
+
+ if (!screen_physical_area_monitor_contains(i, &desired)) {
+ if (i < screen_num_monitors - 1 || found_mon)
+ continue;
+
+ /* the window is not inside any monitor! so just use the first
+ one */
+ a = screen_area(self->desktop, 0, NULL);
+ } else {
+ found_mon = TRUE;
+ a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+ }
+
+ /* This makes sure windows aren't entirely outside of the screen so you
+ can't see them at all.
+ It makes sure 10% of the window is on the screen at least. And don't
+ let it move itself off the top of the screen, which would hide the
+ titlebar on you. (The user can still do this if they want too, it's
+ only limiting the application.
+ */
+ if (client_normal(self)) {
+ if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
+ *x = a->x + a->width - fw/10;
+ if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
+ *y = a->y + a->height - fh/10;
+ if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
+ *x = a->x - fw*9/10;
+ if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
+ *y = a->y - fh*9/10;
+ }
+
+ /* This here doesn't let windows even a pixel outside the
+ struts/screen. When called from client_manage, programs placing
+ themselves are forced completely onscreen, while things like
+ xterm -geometry resolution-width/2 will work fine. Trying to
+ place it completely offscreen will be handled in the above code.
+ Sorry for this confused comment, i am tired. */
+ if (rudel && !self->strut.left && *x < a->x) *x = a->x;
+ if (ruder && !self->strut.right && *x + fw > a->x + a->width)
+ *x = a->x + MAX(0, a->width - fw);
+
+ if (rudet && !self->strut.top && *y < a->y) *y = a->y;
+ if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
+ *y = a->y + MAX(0, a->height - fh);
+
+ g_slice_free(Rect, a);
+ }
+
+ /* get where the client should be */
+ frame_frame_gravity(self->frame, x, y);
+
+ return ox != *x || oy != *y;
+}
+
+static void client_get_all(ObClient *self, gboolean real)
+{
+ /* this is needed for the frame to set itself up */
+ client_get_area(self);
+
+ /* these things can change the decor and functions of the window */
+
+ client_get_mwm_hints(self);
+ /* this can change the mwmhints for special cases */
+ client_get_type_and_transientness(self);
+ client_update_normal_hints(self);
+
+ /* set up the maximum possible decor/functions */
+ client_setup_default_decor_and_functions(self);
+
+ client_get_state(self);
+
+ /* get the session related properties, these can change decorations
+ from per-app settings */
+ client_get_session_ids(self);
+
+ /* get this early so we have it for debugging, also this can be used
+ by app rule matching */
+ client_update_title(self);
+
+ /* now we got everything that can affect the decorations or app rule
+ matching */
+ if (!real)
+ return;
+
+ /* save the values of the variables used for app rule matching */
+ client_save_app_rule_values(self);
+
+ client_update_protocols(self);
+
+ client_update_wmhints(self);
+ /* this may have already been called from client_update_wmhints */
+ if (!self->parents && !self->transient_for_group)
+ client_update_transient_for(self);
+
+ client_get_startup_id(self);
+ client_get_desktop(self);/* uses transient data/group/startup id if a
+ desktop is not specified */
+ client_get_shaped(self);
+
+ {
+ /* a couple type-based defaults for new windows */
+
+ /* this makes sure that these windows appear on all desktops */
+ if (self->type == OB_CLIENT_TYPE_DESKTOP)
+ self->desktop = DESKTOP_ALL;
+ }
+
+#ifdef SYNC
+ client_update_sync_request_counter(self);
+#endif
+
+ client_get_colormap(self);
+ client_update_strut(self);
+ client_update_icons(self);
+ client_update_icon_geometry(self);
+}
+
+static void client_get_startup_id(ObClient *self)
+{
+ if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
+ if (self->group)
+ OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
+ &self->startup_id);
+}
+
+static void client_get_area(ObClient *self)
+{
+ XWindowAttributes wattrib;
+ Status ret;
+
+ ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
+ g_assert(ret != BadWindow);
+
+ RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+ POINT_SET(self->root_pos, wattrib.x, wattrib.y);
+ self->border_width = wattrib.border_width;
+
+ ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
+ wattrib.width, wattrib.height, wattrib.border_width);
+}
+
+static void client_get_desktop(ObClient *self)
+{
+ guint32 d = screen_num_desktops; /* an always-invalid value */
+
+ if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
+ if (d >= screen_num_desktops && d != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ else
+ self->desktop = d;
+ ob_debug("client requested desktop 0x%x", self->desktop);
+ } else {
+ GSList *it;
+ gboolean first = TRUE;
+ guint all = screen_num_desktops; /* not a valid value */
+
+ /* if they are all on one desktop, then open it on the
+ same desktop */
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (c->desktop == DESKTOP_ALL) continue;
+
+ if (first) {
+ all = c->desktop;
+ first = FALSE;
+ }
+ else if (all != c->desktop)
+ all = screen_num_desktops; /* make it invalid */
+ }
+ if (all != screen_num_desktops) {
+ self->desktop = all;
+
+ ob_debug("client desktop set from parents: 0x%x",
+ self->desktop);
+ }
+ /* try get from the startup-notification protocol */
+ else if (sn_get_desktop(self->startup_id, &self->desktop)) {
+ if (self->desktop >= screen_num_desktops &&
+ self->desktop != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ ob_debug("client desktop set from startup-notification: 0x%x",
+ self->desktop);
+ }
+ /* defaults to the current desktop */
+ else {
+ self->desktop = screen_desktop;
+ ob_debug("client desktop set to the current desktop: %d",
+ self->desktop);
+ }
+ }
+}
+
+static void client_get_state(ObClient *self)
+{
+ guint32 *state;
+ guint num;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
+ gulong i;
+ for (i = 0; i < num; ++i) {
+ if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+ self->modal = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+ self->shaded = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
+ self->iconic = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
+ self->skip_taskbar = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
+ self->skip_pager = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
+ self->fullscreen = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+ self->max_vert = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+ self->max_horz = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+ self->above = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+ self->below = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
+ self->demands_attention = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+ self->undecorated = TRUE;
+ }
+
+ g_free(state);
+ }
+}
+
+static void client_get_shaped(ObClient *self)
+{
+ self->shaped = FALSE;
+#ifdef SHAPE
+ if (obt_display_extension_shape) {
+ gint foo;
+ guint ufoo;
+ gint s;
+
+ XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
+
+ XShapeQueryExtents(obt_display, self->window, &s, &foo,
+ &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
+ &ufoo);
+ self->shaped = !!s;
+ }
+#endif
+}
+
+void client_update_transient_for(ObClient *self)
+{
+ Window t = None;
+ ObClient *target = NULL;
+ gboolean trangroup = FALSE;
+
+ if (XGetTransientForHint(obt_display, self->window, &t)) {
+ if (t != self->window) { /* can't be transient to itself! */
+ ObWindow *tw = window_find(t);
+ /* if this happens then we need to check for it */
+ g_assert(tw != CLIENT_AS_WINDOW(self));
+ if (tw && WINDOW_IS_CLIENT(tw)) {
+ /* watch out for windows with a parent that is something
+ different, like a dockapp for example */
+ target = WINDOW_AS_CLIENT(tw);
+ }
+ }
+
+ /* Setting the transient_for to Root is actually illegal, however
+ applications from time have done this to specify transient for
+ their group */
+ if (!target && self->group && t == obt_root(ob_screen))
+ trangroup = TRUE;
+ } else if (self->group && self->transient)
+ trangroup = TRUE;
+
+ client_update_transient_tree(self, self->group, self->group,
+ self->transient_for_group, trangroup,
+ client_direct_parent(self), target);
+ self->transient_for_group = trangroup;
+
+}
+
+static void client_update_transient_tree(ObClient *self,
+ ObGroup *oldgroup, ObGroup *newgroup,
+ gboolean oldgtran, gboolean newgtran,
+ ObClient* oldparent,
+ ObClient *newparent)
+{
+ GSList *it, *next;
+ ObClient *c;
+
+ g_assert(!oldgtran || oldgroup);
+ g_assert(!newgtran || newgroup);
+ g_assert((!oldgtran && !oldparent) ||
+ (oldgtran && !oldparent) ||
+ (!oldgtran && oldparent));
+ g_assert((!newgtran && !newparent) ||
+ (newgtran && !newparent) ||
+ (!newgtran && newparent));
+
+ /* * *
+ Group transient windows are not allowed to have other group
+ transient windows as their children.
+ * * */
+
+ /* No change has occured */
+ if (oldgroup == newgroup &&
+ oldgtran == newgtran &&
+ oldparent == newparent) return;
+
+ /** Remove the client from the transient tree **/
+
+ for (it = self->transients; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->transients = g_slist_delete_link(self->transients, it);
+ c->parents = g_slist_remove(c->parents, self);
+ }
+ for (it = self->parents; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->parents = g_slist_delete_link(self->parents, it);
+ c->transients = g_slist_remove(c->transients, self);
+ }
+
+ /** Re-add the client to the transient tree **/
+
+ /* If we're transient for a group then we need to add ourselves to all our
+ parents */
+ if (newgtran) {
+ for (it = newgroup->members; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (c != self &&
+ !client_search_top_direct_parent(c)->transient_for_group &&
+ client_normal(c))
+ {
+ c->transients = g_slist_prepend(c->transients, self);
+ self->parents = g_slist_prepend(self->parents, c);
+ }
+ }
+ }
+
+ /* If we are now transient for a single window we need to add ourselves to
+ its children
+
+ WARNING: Cyclical transient-ness is possible if two windows are
+ transient for eachother.
+ */
+ else if (newparent &&
+ /* don't make ourself its child if it is already our child */
+ !client_is_direct_child(self, newparent) &&
+ client_normal(newparent))
+ {
+ newparent->transients = g_slist_prepend(newparent->transients, self);
+ self->parents = g_slist_prepend(self->parents, newparent);
+ }
+
+ /* Add any group transient windows to our children. But if we're transient
+ for the group, then other group transients are not our children.
+
+ WARNING: Cyclical transient-ness is possible. For e.g. if:
+ A is transient for the group
+ B is transient for A
+ C is transient for B
+ A can't be transient for C or we have a cycle
+ */
+ if (!newgtran && newgroup &&
+ (!newparent ||
+ !client_search_top_direct_parent(newparent)->transient_for_group) &&
+ client_normal(self))
+ {
+ for (it = newgroup->members; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (c != self && c->transient_for_group &&
+ /* Don't make it our child if it is already our parent */
+ !client_is_direct_child(c, self))
+ {
+ self->transients = g_slist_prepend(self->transients, c);
+ c->parents = g_slist_prepend(c->parents, self);
+ }
+ }
+ }
+
+ /** If we change our group transient-ness, our children change their
+ effective group transient-ness, which affects how they relate to other
+ group windows **/
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (!c->transient_for_group)
+ client_update_transient_tree(c, c->group, c->group,
+ c->transient_for_group,
+ c->transient_for_group,
+ client_direct_parent(c),
+ client_direct_parent(c));
+ }
+}
+
+void client_get_mwm_hints(ObClient *self)
+{
+ guint num;
+ guint32 *hints;
+
+ self->mwmhints.flags = 0; /* default to none */
+
+ if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
+ &hints, &num)) {
+ if (num >= OB_MWM_ELEMENTS) {
+ self->mwmhints.flags = hints[0];
+ self->mwmhints.functions = hints[1];
+ self->mwmhints.decorations = hints[2];
+ }
+ g_free(hints);
+ }
+}
+
+void client_get_type_and_transientness(ObClient *self)
+{
+ guint num, i;
+ guint32 *val;
+ Window t;
+
+ self->type = -1;
+ self->transient = FALSE;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
+ /* use the first value that we know about in the array */
+ for (i = 0; i < num; ++i) {
+ if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
+ self->type = OB_CLIENT_TYPE_DESKTOP;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
+ self->type = OB_CLIENT_TYPE_DOCK;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
+ self->type = OB_CLIENT_TYPE_TOOLBAR;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
+ self->type = OB_CLIENT_TYPE_MENU;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
+ self->type = OB_CLIENT_TYPE_UTILITY;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
+ self->type = OB_CLIENT_TYPE_SPLASH;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
+ self->type = OB_CLIENT_TYPE_DIALOG;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
+ self->type = OB_CLIENT_TYPE_NORMAL;
+ else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
+ {
+ /* prevent this window from getting any decor or
+ functionality */
+ self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
+ OB_MWM_FLAG_DECORATIONS);
+ self->mwmhints.decorations = 0;
+ self->mwmhints.functions = 0;
+ }
+ if (self->type != (ObClientType) -1)
+ break; /* grab the first legit type */
+ }
+ g_free(val);
+ }
+
+ if (XGetTransientForHint(obt_display, self->window, &t))
+ self->transient = TRUE;
+
+ if (self->type == (ObClientType) -1) {
+ /*the window type hint was not set, which means we either classify
+ ourself as a normal window or a dialog, depending on if we are a
+ transient. */
+ if (self->transient)
+ self->type = OB_CLIENT_TYPE_DIALOG;
+ else
+ self->type = OB_CLIENT_TYPE_NORMAL;
+ }
+
+ /* then, based on our type, we can update our transientness.. */
+ if (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_UTILITY)
+ {
+ self->transient = TRUE;
+ }
+}
+
+void client_update_protocols(ObClient *self)
+{
+ guint32 *proto;
+ guint num_ret, i;
+
+ self->focus_notify = FALSE;
+ self->delete_window = FALSE;
+
+ if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
+ for (i = 0; i < num_ret; ++i) {
+ if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
+ /* this means we can request the window to close */
+ self->delete_window = TRUE;
+ else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
+ /* if this protocol is requested, then the window will be
+ notified whenever we want it to receive focus */
+ self->focus_notify = TRUE;
+ else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
+ /* if this protocol is requested, then the window will allow
+ pings to determine if it is still alive */
+ self->ping = TRUE;
+#ifdef SYNC
+ else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
+ /* if this protocol is requested, then resizing the
+ window will be synchronized between the frame and the
+ client */
+ self->sync_request = TRUE;
+#endif
+ }
+ g_free(proto);
+ }
+}
+
+#ifdef SYNC
+void client_update_sync_request_counter(ObClient *self)
+{
+ guint32 i;
+
+ if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
+ {
+ XSyncValue val;
+
+ self->sync_counter = i;
+
+ /* this must be set when managing a new window according to EWMH */
+ XSyncIntToValue(&val, 0);
+ XSyncSetCounter(obt_display, self->sync_counter, val);
+ } else
+ self->sync_counter = None;
+}
+#endif
+
+static void client_get_colormap(ObClient *self)
+{
+ XWindowAttributes wa;
+
+ if (XGetWindowAttributes(obt_display, self->window, &wa))
+ client_update_colormap(self, wa.colormap);
+}
+
+void client_update_colormap(ObClient *self, Colormap colormap)
+{
+ if (colormap == self->colormap) return;
+
+ ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
+
+ if (client_focused(self)) {
+ screen_install_colormap(self, FALSE); /* uninstall old one */
+ self->colormap = colormap;
+ screen_install_colormap(self, TRUE); /* install new one */
+ } else
+ self->colormap = colormap;
+}
+
+void client_update_opacity(ObClient *self)
+{
+ guint32 o;
+
+ if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
+ OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
+ else
+ OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
+}
+
+void client_update_normal_hints(ObClient *self)
+{
+ XSizeHints size;
+ glong ret;
+
+ /* defaults */
+ self->min_ratio = 0.0f;
+ self->max_ratio = 0.0f;
+ SIZE_SET(self->size_inc, 1, 1);
+ SIZE_SET(self->base_size, -1, -1);
+ SIZE_SET(self->min_size, 0, 0);
+ SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
+
+ /* get the hints from the window */
+ if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
+ /* normal windows can't request placement! har har
+ if (!client_normal(self))
+ */
+ self->positioned = (size.flags & (PPosition|USPosition));
+ self->sized = (size.flags & (PSize|USSize));
+
+ if (size.flags & PWinGravity)
+ self->gravity = size.win_gravity;
+
+ if (size.flags & PAspect) {
+ if (size.min_aspect.y)
+ self->min_ratio =
+ (gfloat) size.min_aspect.x / size.min_aspect.y;
+ if (size.max_aspect.y)
+ self->max_ratio =
+ (gfloat) size.max_aspect.x / size.max_aspect.y;
+ }
+
+ if (size.flags & PMinSize)
+ SIZE_SET(self->min_size, size.min_width, size.min_height);
+
+ if (size.flags & PMaxSize)
+ SIZE_SET(self->max_size, size.max_width, size.max_height);
+
+ if (size.flags & PBaseSize)
+ SIZE_SET(self->base_size, size.base_width, size.base_height);
+
+ if (size.flags & PResizeInc && size.width_inc && size.height_inc)
+ SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
+
+ ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
+ self->min_size.width, self->min_size.height,
+ self->max_size.width, self->max_size.height);
+ ob_debug("size inc (%d %d) base size (%d %d)",
+ self->size_inc.width, self->size_inc.height,
+ self->base_size.width, self->base_size.height);
+ }
+ else
+ ob_debug("Normal hints: not set");
+}
+
+static void client_setup_default_decor_and_functions(ObClient *self)
+{
+ /* start with everything (cept fullscreen) */
+ self->decorations =
+ (OB_FRAME_DECOR_TITLEBAR |
+ OB_FRAME_DECOR_HANDLE |
+ OB_FRAME_DECOR_GRIPS |
+ OB_FRAME_DECOR_BORDER |
+ OB_FRAME_DECOR_ICON |
+ OB_FRAME_DECOR_ALLDESKTOPS |
+ OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE |
+ OB_FRAME_DECOR_SHADE |
+ OB_FRAME_DECOR_CLOSE);
+ self->functions =
+ (OB_CLIENT_FUNC_RESIZE |
+ OB_CLIENT_FUNC_MOVE |
+ OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE |
+ OB_CLIENT_FUNC_SHADE |
+ OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_BELOW |
+ OB_CLIENT_FUNC_ABOVE |
+ OB_CLIENT_FUNC_UNDECORATE);
+
+ if (!(self->min_size.width < self->max_size.width ||
+ self->min_size.height < self->max_size.height))
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ /* normal windows retain all of the possible decorations and
+ functionality, and can be fullscreen */
+ self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+ break;
+
+ case OB_CLIENT_TYPE_DIALOG:
+ /* sometimes apps make dialog windows fullscreen for some reason (for
+ e.g. kpdf does this..) */
+ self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+ break;
+
+ case OB_CLIENT_TYPE_UTILITY:
+ /* these windows don't have anything added or removed by default */
+ break;
+
+ case OB_CLIENT_TYPE_MENU:
+ case OB_CLIENT_TYPE_TOOLBAR:
+ /* these windows can't iconify or maximize */
+ self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE);
+ self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE);
+ break;
+
+ case OB_CLIENT_TYPE_SPLASH:
+ /* these don't get get any decorations, and the only thing you can
+ do with them is move them */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_MOVE;
+ break;
+
+ case OB_CLIENT_TYPE_DESKTOP:
+ /* these windows are not manipulated by the window manager */
+ self->decorations = 0;
+ self->functions = 0;
+ break;
+
+ case OB_CLIENT_TYPE_DOCK:
+ /* these windows are not manipulated by the window manager, but they
+ can set below layer which has a special meaning */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_BELOW;
+ break;
+ }
+
+ /* If the client has no decor from its type (which never changes) then
+ don't allow the user to "undecorate" the window. Otherwise, allow them
+ to, even if there are motif hints removing the decor, because those
+ may change these days (e.g. chromium) */
+ if (self->decorations == 0)
+ self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
+
+ /* Mwm Hints are applied subtractively to what has already been chosen for
+ decor and functionality */
+ if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
+ if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
+ if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
+ (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+ {
+ /* if the mwm hints request no handle or title, then all
+ decorations are disabled, but keep the border if that's
+ specified */
+ if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
+ self->decorations = OB_FRAME_DECOR_BORDER;
+ else
+ self->decorations = 0;
+ }
+ }
+ }
+
+ if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
+ self->functions &= ~OB_CLIENT_FUNC_MOVE;
+ /* dont let mwm hints kill any buttons
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
+ self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
+ self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ */
+ /* dont let mwm hints kill the close button
+ if (! (self->mwmhints.functions & MwmFunc_Close))
+ self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
+ }
+ }
+
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE))
+ self->decorations &= ~OB_FRAME_DECOR_SHADE;
+ if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
+ self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
+ if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
+ self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
+
+ /* can't maximize without moving/resizing */
+ if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
+ (self->functions & OB_CLIENT_FUNC_MOVE) &&
+ (self->functions & OB_CLIENT_FUNC_RESIZE))) {
+ self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
+ }
+}
+
+/*! Set up decor for a client based on its undecorated state. */
+static void client_setup_decor_undecorated(ObClient *self)
+{
+ /* If the user requested no decorations, then remove all the decorations,
+ except the border. But don't add a border if there wasn't one. */
+ if (self->undecorated)
+ self->decorations &= (config_theme_keepborder ?
+ OB_FRAME_DECOR_BORDER : 0);
+}
+
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+{
+ client_setup_default_decor_and_functions(self);
+
+ client_setup_decor_undecorated(self);
+
+ if (self->max_horz && self->max_vert) {
+ /* once upon a time you couldn't resize maximized windows, that is not
+ the case any more though !
+
+ but do kill the handle on fully maxed windows */
+ self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+ }
+
+ /* if we don't have a titlebar, then we cannot shade! */
+ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ self->functions &= ~OB_CLIENT_FUNC_SHADE;
+
+ /* now we need to check against rules for the client's current state */
+ if (self->fullscreen) {
+ self->functions &= (OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_FULLSCREEN |
+ OB_CLIENT_FUNC_ICONIFY);
+ self->decorations = 0;
+ }
+
+ client_change_allowed_actions(self);
+
+ if (reconfig)
+ /* reconfigure to make sure decorations are updated */
+ client_reconfigure(self, FALSE);
+}
+
+static void client_change_allowed_actions(ObClient *self)
+{
+ gulong actions[12];
+ gint num = 0;
+
+ /* desktop windows are kept on all desktops */
+ if (self->type != OB_CLIENT_TYPE_DESKTOP)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
+
+ if (self->functions & OB_CLIENT_FUNC_SHADE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
+ if (self->functions & OB_CLIENT_FUNC_CLOSE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
+ if (self->functions & OB_CLIENT_FUNC_MOVE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
+ if (self->functions & OB_CLIENT_FUNC_ICONIFY)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
+ if (self->functions & OB_CLIENT_FUNC_RESIZE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
+ if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
+ if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
+ }
+ if (self->functions & OB_CLIENT_FUNC_ABOVE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
+ if (self->functions & OB_CLIENT_FUNC_BELOW)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
+ if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+ actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
+
+ OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
+
+ /* make sure the window isn't breaking any rules now
+
+ don't check ICONIFY here. just cuz a window can't iconify doesnt mean
+ it can't be iconified with its parent
+ */
+
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
+ if (self->frame) client_shade(self, FALSE);
+ else self->shaded = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
+ if (self->frame) client_fullscreen(self, FALSE);
+ else self->fullscreen = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
+ self->max_vert)) {
+ if (self->frame) client_maximize(self, FALSE, 0);
+ else self->max_vert = self->max_horz = FALSE;
+ }
+}
+
+void client_update_wmhints(ObClient *self)
+{
+ XWMHints *hints;
+
+ /* assume a window takes input if it doesn't specify */
+ self->can_focus = TRUE;
+
+ if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
+ gboolean ur;
+
+ if (hints->flags & InputHint)
+ self->can_focus = hints->input;
+
+ /* only do this when first managing the window *AND* when we aren't
+ starting up! */
+ if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
+ if (hints->flags & StateHint)
+ self->iconic = hints->initial_state == IconicState;
+
+ ur = self->urgent;
+ self->urgent = (hints->flags & XUrgencyHint);
+ if (self->urgent && !ur)
+ client_hilite(self, TRUE);
+ else if (!self->urgent && ur && self->demands_attention)
+ client_hilite(self, FALSE);
+
+ if (!(hints->flags & WindowGroupHint))
+ hints->window_group = None;
+
+ /* did the group state change? */
+ if (hints->window_group !=
+ (self->group ? self->group->leader : None))
+ {
+ ObGroup *oldgroup = self->group;
+
+ /* remove from the old group if there was one */
+ if (self->group) {
+ group_remove(self->group, self);
+ self->group = NULL;
+ }
+
+ /* add ourself to the group if we have one */
+ if (hints->window_group != None) {
+ self->group = group_add(hints->window_group, self);
+ }
+
+ /* Put ourselves into the new group's transient tree, and remove
+ ourselves from the old group's */
+ client_update_transient_tree(self, oldgroup, self->group,
+ self->transient_for_group,
+ self->transient_for_group,
+ client_direct_parent(self),
+ client_direct_parent(self));
+
+ /* Lastly, being in a group, or not, can change if the window is
+ transient for anything.
+
+ The logic for this is:
+ self->transient = TRUE always if the window wants to be
+ transient for something, even if transient_for was NULL because
+ it wasn't in a group before.
+
+ If parents was NULL and oldgroup was NULL we can assume
+ that when we add the new group, it will become transient for
+ something.
+
+ If transient_for_group is TRUE, then it must have already
+ had a group. If it is getting a new group, the above call to
+ client_update_transient_tree has already taken care of
+ everything ! If it is losing all group status then it will
+ no longer be transient for anything and that needs to be
+ updated.
+ */
+ if (self->transient &&
+ ((self->parents == NULL && oldgroup == NULL) ||
+ (self->transient_for_group && !self->group)))
+ client_update_transient_for(self);
+ }
+
+ /* the WM_HINTS can contain an icon */
+ if (hints->flags & IconPixmapHint)
+ client_update_icons(self);
+
+ XFree(hints);
+ }
+
+ focus_cycle_addremove(self, TRUE);
+}
+
+void client_update_title(ObClient *self)
+{
+ gchar *data = NULL;
+ gchar *visible = NULL;
+
+ g_free(self->title);
+ g_free(self->original_title);
+
+ /* try netwm */
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
+ /* try old x stuff */
+ if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
+ if (self->transient) {
+ /*
+ GNOME alert windows are not given titles:
+ http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+ */
+ data = g_strdup("");
+ } else
+ data = g_strdup(_("Unnamed Window"));
+ }
+ }
+ self->original_title = g_strdup(data);
+
+ if (self->client_machine) {
+ visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+ g_free(data);
+ } else
+ visible = data;
+
+ if (self->not_responding) {
+ data = visible;
+ if (self->kill_level > 0)
+ visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+ else
+ visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+ g_free(data);
+ }
+
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
+ self->title = visible;
+
+ if (self->frame)
+ frame_adjust_title(self->frame);
+
+ /* update the icon title */
+ data = NULL;
+ g_free(self->icon_title);
+
+ /* try netwm */
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
+ /* try old x stuff */
+ if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
+ data = g_strdup(self->title);
+
+ if (self->client_machine) {
+ visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+ g_free(data);
+ } else
+ visible = data;
+
+ if (self->not_responding) {
+ data = visible;
+ if (self->kill_level > 0)
+ visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+ else
+ visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+ g_free(data);
+ }
+
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
+ self->icon_title = visible;
+}
+
+void client_update_strut(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+ gboolean got = FALSE;
+ StrutPartial strut;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
+ &data, &num))
+ {
+ if (num == 12) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ data[4], data[5], data[8], data[9],
+ data[6], data[7], data[10], data[11]);
+ }
+ g_free(data);
+ }
+
+ if (!got &&
+ OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
+ if (num == 4) {
+ const Rect *a;
+
+ got = TRUE;
+
+ /* use the screen's width/height */
+ a = screen_physical_area_all_monitors();
+
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ a->y, a->y + a->height - 1,
+ a->x, a->x + a->width - 1,
+ a->y, a->y + a->height - 1,
+ a->x, a->x + a->width - 1);
+ }
+ g_free(data);
+ }
+
+ if (!got)
+ STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
+ self->strut = strut;
+
+ /* updating here is pointless while we're being mapped cuz we're not in
+ the client list yet */
+ if (self->frame)
+ screen_update_areas();
+ }
+}
+
+void client_update_icons(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+ guint w, h, i, j;
+ RrImage *img;
+
+ img = NULL;
+
+ /* grab the server, because we might be setting the window's icon and
+ we don't want them to set it in between and we overwrite their own
+ icon */
+ grab_server(TRUE);
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
+ /* figure out how many valid icons are in here */
+ i = 0;
+ while (i + 2 < num) { /* +2 is to make sure there is a w and h */
+ w = data[i++];
+ h = data[i++];
+ /* watch for the data being too small for the specified size,
+ or for zero sized icons. */
+ if (i + w*h > num || w == 0 || h == 0) {
+ i += w*h;
+ continue;
+ }
+
+ /* convert it to the right bit order for ObRender */
+ for (j = 0; j < w*h; ++j)
+ data[i+j] =
+ (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
+ (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
+ (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
+ (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
+
+ /* add it to the image cache as an original */
+ if (!img)
+ img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
+ else
+ RrImageAddFromData(img, &data[i], w, h);
+
+ i += w*h;
+ }
+
+ g_free(data);
+ }
+
+ /* if we didn't find an image from the NET_WM_ICON stuff, then try the
+ legacy X hints */
+ if (!img) {
+ XWMHints *hints;
+
+ if ((hints = XGetWMHints(obt_display, self->window))) {
+ if (hints->flags & IconPixmapHint) {
+ gboolean xicon;
+ obt_display_ignore_errors(TRUE);
+ xicon = RrPixmapToRGBA(ob_rr_inst,
+ hints->icon_pixmap,
+ (hints->flags & IconMaskHint ?
+ hints->icon_mask : None),
+ (gint*)&w, (gint*)&h, &data);
+ obt_display_ignore_errors(FALSE);
+
+ if (xicon) {
+ if (w > 0 && h > 0) {
+ if (!img)
+ img = RrImageNewFromData(ob_rr_icons, data, w, h);
+ else
+ RrImageAddFromData(img, data, w, h);
+ }
+
+ g_free(data);
+ }
+ }
+ XFree(hints);
+ }
+ }
+
+ /* set the client's icons to be whatever we found */
+ RrImageUnref(self->icon_set);
+ self->icon_set = img;
+
+ /* if the client has no icon at all, then we set a default icon onto it.
+ but, if it has parents, then one of them will have an icon already
+ */
+ if (!self->icon_set && !self->parents) {
+ RrPixel32 *icon = ob_rr_theme->def_win_icon;
+ gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
+
+ w = ob_rr_theme->def_win_icon_w;
+ h = ob_rr_theme->def_win_icon_h;
+ ldata = g_new(gulong, w*h+2);
+ ldata[0] = w;
+ ldata[1] = h;
+ for (i = 0; i < w*h; ++i)
+ ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
+ (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
+ (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
+ (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
+ OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
+ g_free(ldata);
+ } else if (self->frame)
+ /* don't draw the icon empty if we're just setting one now anyways,
+ we'll get the property change any second */
+ frame_adjust_icon(self->frame);
+
+ grab_server(FALSE);
+}
+
+void client_update_icon_geometry(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+
+ RECT_SET(self->icon_geometry, 0, 0, 0, 0);
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
+ &data, &num))
+ {
+ if (num == 4)
+ /* don't let them set it with an area < 0 */
+ RECT_SET(self->icon_geometry, data[0], data[1],
+ MAX(data[2],0), MAX(data[3],0));
+ g_free(data);
+ }
+}
+
+static void client_get_session_ids(ObClient *self)
+{
+ guint32 leader;
+ gboolean got;
+ gchar *s;
+ gchar **ss;
+
+ if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
+ leader = None;
+
+ /* get the SM_CLIENT_ID */
+ if (leader && leader != self->window)
+ OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
+ else
+ OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
+
+ /* get the WM_CLASS (name and class). make them "" if they are not
+ provided */
+ got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
+
+ if (got) {
+ if (ss[0]) {
+ self->name = g_strdup(ss[0]);
+ if (ss[1])
+ self->class = g_strdup(ss[1]);
+ }
+ g_strfreev(ss);
+ }
+
+ if (self->name == NULL) self->name = g_strdup("");
+ if (self->class == NULL) self->class = g_strdup("");
+
+ /* get the WM_CLASS (name and class) from the group leader. make them "" if
+ they are not provided */
+ if (leader)
+ got = OBT_PROP_GETSS_TYPE(leader, WM_CLASS, STRING_NO_CC, &ss);
+ else
+ got = FALSE;
+
+ if (got) {
+ if (ss[0]) {
+ self->group_name = g_strdup(ss[0]);
+ if (ss[1])
+ self->group_class = g_strdup(ss[1]);
+ }
+ g_strfreev(ss);
+ }
+
+ if (self->group_name == NULL) self->group_name = g_strdup("");
+ if (self->group_class == NULL) self->group_class = g_strdup("");
+
+ /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
+ got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
+
+ if (got)
+ self->role = s;
+ else
+ self->role = g_strdup("");
+
+ /* get the WM_COMMAND */
+ got = FALSE;
+
+ if (leader)
+ got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
+ if (!got)
+ got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
+
+ if (got) {
+ /* merge/mash them all together */
+ gchar *merge = NULL;
+ gint i;
+
+ for (i = 0; ss[i]; ++i) {
+ gchar *tmp = merge;
+ if (merge)
+ merge = g_strconcat(merge, ss[i], NULL);
+ else
+ merge = g_strconcat(ss[i], NULL);
+ g_free(tmp);
+ }
+ g_strfreev(ss);
+
+ self->wm_command = merge;
+ }
+
+ /* get the WM_CLIENT_MACHINE */
+ got = FALSE;
+ if (leader)
+ got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
+ if (!got)
+ got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
+
+ if (got) {
+ gchar localhost[128];
+ guint32 pid;
+
+ gethostname(localhost, 127);
+ localhost[127] = '\0';
+ if (strcmp(localhost, s) != 0)
+ self->client_machine = s;
+ else
+ g_free(s);
+
+ /* see if it has the PID set too (the PID requires that the
+ WM_CLIENT_MACHINE be set) */
+ if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
+ self->pid = pid;
+ }
+}
+
+const gchar *client_type_to_string(ObClient *self)
+{
+ const gchar *type;
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ type = "normal"; break;
+ case OB_CLIENT_TYPE_DIALOG:
+ type = "dialog"; break;
+ case OB_CLIENT_TYPE_UTILITY:
+ type = "utility"; break;
+ case OB_CLIENT_TYPE_MENU:
+ type = "menu"; break;
+ case OB_CLIENT_TYPE_TOOLBAR:
+ type = "toolbar"; break;
+ case OB_CLIENT_TYPE_SPLASH:
+ type = "splash"; break;
+ case OB_CLIENT_TYPE_DESKTOP:
+ type = "desktop"; break;
+ case OB_CLIENT_TYPE_DOCK:
+ type = "dock"; break;
+ }
+
+ return type;
+}
+
+/*! Save the properties used for app matching rules, as seen by Openbox when
+ the window mapped, so that users can still access them later if the app
+ changes them */
+static void client_save_app_rule_values(ObClient *self)
+{
+ OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
+ OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
+ OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_NAME, self->group_name);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_CLASS, self->group_class);
+ OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
+
+ OBT_PROP_SETS(self->window, OB_APP_TYPE, client_type_to_string(self));
+}
+
+static void client_change_wm_state(ObClient *self)
+{
+ gulong state[2];
+ glong old;
+
+ old = self->wmstate;
+
+ if (self->shaded || self->iconic ||
+ (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
+ {
+ self->wmstate = IconicState;
+ } else
+ self->wmstate = NormalState;
+
+ if (old != self->wmstate) {
+ OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
+ self->wmstate, 1, 0, 0, 0);
+
+ state[0] = self->wmstate;
+ state[1] = None;
+ OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
+ }
+}
+
+static void client_change_state(ObClient *self)
+{
+ gulong netstate[12];
+ guint num;
+
+ num = 0;
+ if (self->modal)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
+ if (self->shaded)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
+ if (self->iconic)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
+ if (self->skip_taskbar)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
+ if (self->skip_pager)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
+ if (self->fullscreen)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
+ if (self->max_vert)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
+ if (self->max_horz)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
+ if (self->above)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
+ if (self->below)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
+ if (self->demands_attention)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
+ if (self->undecorated)
+ netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
+ OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
+
+ if (self->frame)
+ frame_adjust_state(self->frame);
+}
+
+ObClient *client_search_focus_tree(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret;
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ if (client_focused(it->data)) return it->data;
+ if ((ret = client_search_focus_tree(it->data))) return ret;
+ }
+ return NULL;
+}
+
+ObClient *client_search_focus_tree_full(ObClient *self)
+{
+ if (self->parents) {
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if ((c = client_search_focus_tree_full(c))) return c;
+ }
+
+ return NULL;
+ }
+ else {
+ /* this function checks the whole tree, the client_search_focus_tree
+ does not, so we need to check this window */
+ if (client_focused(self))
+ return self;
+ return client_search_focus_tree(self);
+ }
+}
+
+ObClient *client_search_focus_group_full(ObClient *self)
+{
+ GSList *it;
+
+ if (self->group) {
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (client_focused(c)) return c;
+ if ((c = client_search_focus_tree(it->data))) return c;
+ }
+ } else
+ if (client_focused(self)) return self;
+ return NULL;
+}
+
+gboolean client_has_parent(ObClient *self)
+{
+ return self->parents != NULL;
+}
+
+gboolean client_has_children(ObClient *self)
+{
+ return self->transients != NULL;
+}
+
+gboolean client_is_oldfullscreen(const ObClient *self,
+ const Rect *area)
+{
+ const Rect *monitor, *allmonitors;
+
+ /* No decorations and fills the monitor = oldskool fullscreen.
+ But not for maximized windows.
+ */
+
+ if (self->decorations || self->max_horz || self->max_vert) return FALSE;
+
+ monitor = screen_physical_area_monitor(screen_find_monitor(area));
+ allmonitors = screen_physical_area_all_monitors();
+
+ return (RECT_EQUAL(*area, *monitor) ||
+ RECT_EQUAL(*area, *allmonitors));
+}
+
+static ObStackingLayer calc_layer(ObClient *self)
+{
+ ObStackingLayer l;
+
+ if (self->type == OB_CLIENT_TYPE_DESKTOP)
+ l = OB_STACKING_LAYER_DESKTOP;
+ else if (self->type == OB_CLIENT_TYPE_DOCK) {
+ if (self->below) l = OB_STACKING_LAYER_NORMAL;
+ else l = OB_STACKING_LAYER_ABOVE;
+ }
+ else if ((self->fullscreen ||
+ client_is_oldfullscreen(self, &self->area)) &&
+ /* you are fullscreen while you or your children are focused.. */
+ (client_focused(self) || client_search_focus_tree(self) ||
+ /* you can be fullscreen if you're on another desktop */
+ (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL) ||
+ /* and you can also be fullscreen if the focused client is on
+ another monitor, or nothing else is focused */
+ (!focus_client ||
+ client_monitor(focus_client) != client_monitor(self))))
+ l = OB_STACKING_LAYER_FULLSCREEN;
+ else if (self->above) l = OB_STACKING_LAYER_ABOVE;
+ else if (self->below) l = OB_STACKING_LAYER_BELOW;
+ else l = OB_STACKING_LAYER_NORMAL;
+
+ return l;
+}
+
+static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
+ ObStackingLayer min)
+{
+ ObStackingLayer old, own;
+ GSList *it;
+
+ old = self->layer;
+ own = calc_layer(self);
+ self->layer = MAX(own, min);
+
+ if (self->layer != old) {
+ stacking_remove(CLIENT_AS_WINDOW(self));
+ stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ }
+
+ /* we've been restacked */
+ self->visited = TRUE;
+
+ for (it = self->transients; it; it = g_slist_next(it))
+ client_calc_layer_recursive(it->data, orig,
+ self->layer);
+}
+
+static void client_calc_layer_internal(ObClient *self)
+{
+ GSList *sit;
+
+ /* transients take on the layer of their parents */
+ sit = client_search_all_top_parents(self);
+
+ for (; sit; sit = g_slist_next(sit))
+ client_calc_layer_recursive(sit->data, self, 0);
+}
+
+void client_calc_layer(ObClient *self)
+{
+ GList *it;
+
+ /* skip over stuff above fullscreen layer */
+ for (it = stacking_list; it; it = g_list_next(it))
+ if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+ /* find the windows in the fullscreen layer, and mark them not-visited */
+ for (; it; it = g_list_next(it)) {
+ if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+ else if (WINDOW_IS_CLIENT(it->data))
+ WINDOW_AS_CLIENT(it->data)->visited = FALSE;
+ }
+
+ client_calc_layer_internal(self);
+
+ /* skip over stuff above fullscreen layer */
+ for (it = stacking_list; it; it = g_list_next(it))
+ if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+ /* now recalc any windows in the fullscreen layer which have not
+ had their layer recalced already */
+ for (; it; it = g_list_next(it)) {
+ if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+ else if (WINDOW_IS_CLIENT(it->data) &&
+ !WINDOW_AS_CLIENT(it->data)->visited)
+ client_calc_layer_internal(it->data);
+ }
+}
+
+gboolean client_should_show(ObClient *self)
+{
+ if (self->iconic)
+ return FALSE;
+ if (client_normal(self) && screen_showing_desktop())
+ return FALSE;
+ if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean client_show(ObClient *self)
+{
+ gboolean show = FALSE;
+
+ if (client_should_show(self)) {
+ /* replay pending pointer event before showing the window, in case it
+ should be going to something under the window */
+ mouse_replay_pointer();
+
+ frame_show(self->frame);
+ show = TRUE;
+
+ /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+ it needs to be in IconicState. This includes when it is on another
+ desktop!
+ */
+ client_change_wm_state(self);
+ }
+ return show;
+}
+
+gboolean client_hide(ObClient *self)
+{
+ gboolean hide = FALSE;
+
+ if (!client_should_show(self)) {
+ /* We don't need to ignore enter events here.
+ The window can hide/iconify in 3 different ways:
+ 1 - through an x message. in this case we ignore all enter events
+ caused by responding to the x message (unless underMouse)
+ 2 - by a keyboard action. in this case we ignore all enter events
+ caused by the action
+ 3 - by a mouse action. in this case they are doing stuff with the
+ mouse and focus _should_ move.
+
+ Also in action_end, we simulate an enter event that can't be ignored
+ so trying to ignore them is futile in case 3 anyways
+ */
+
+ /* replay pending pointer event before hiding the window, in case it
+ should be going to the window */
+ mouse_replay_pointer();
+
+ frame_hide(self->frame);
+ hide = TRUE;
+
+ /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+ it needs to be in IconicState. This includes when it is on another
+ desktop!
+ */
+ client_change_wm_state(self);
+ }
+ return hide;
+}
+
+void client_showhide(ObClient *self)
+{
+ if (!client_show(self))
+ client_hide(self);
+}
+
+gboolean client_normal(ObClient *self) {
+ return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
+ self->type == OB_CLIENT_TYPE_DOCK ||
+ self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
+gboolean client_helper(ObClient *self)
+{
+ return (self->type == OB_CLIENT_TYPE_UTILITY ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR);
+}
+
+gboolean client_occupies_space(ObClient *self)
+{
+ return !(self->type == OB_CLIENT_TYPE_DESKTOP ||
+ self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
+gboolean client_mouse_focusable(ObClient *self)
+{
+ return !(self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_SPLASH ||
+ self->type == OB_CLIENT_TYPE_DOCK);
+}
+
+gboolean client_enter_focusable(ObClient *self)
+{
+ /* you can focus desktops but it shouldn't on enter */
+ return (client_mouse_focusable(self) &&
+ self->type != OB_CLIENT_TYPE_DESKTOP);
+}
+
+static void client_apply_startup_state(ObClient *self,
+ gint x, gint y, gint w, gint h)
+{
+ /* save the states that we are going to apply */
+ gboolean iconic = self->iconic;
+ gboolean fullscreen = self->fullscreen;
+ gboolean undecorated = self->undecorated;
+ gboolean shaded = self->shaded;
+ gboolean demands_attention = self->demands_attention;
+ gboolean max_horz = self->max_horz;
+ gboolean max_vert = self->max_vert;
+ Rect oldarea;
+ gint l;
+
+ /* turn them all off in the client, so they won't affect the window
+ being placed */
+ self->iconic = self->fullscreen = self->undecorated = self->shaded =
+ self->demands_attention = self->max_horz = self->max_vert = FALSE;
+
+ /* move the client to its placed position, or it it's already there,
+ generate a ConfigureNotify telling the client where it is.
+
+ do this after adjusting the frame. otherwise it gets all weird and
+ clients don't work right
+
+ do this before applying the states so they have the correct
+ pre-max/pre-fullscreen values
+ */
+ client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
+ ob_debug("placed window 0x%x at %d, %d with size %d x %d",
+ self->window, x, y, w, h);
+ /* save the area, and make it where it should be for the premax stuff */
+ oldarea = self->area;
+ RECT_SET(self->area, x, y, w, h);
+
+ /* apply the states. these are in a carefully crafted order.. */
+
+ if (iconic)
+ client_iconify(self, TRUE, FALSE, TRUE);
+ if (undecorated)
+ client_set_undecorated(self, TRUE);
+ if (shaded)
+ client_shade(self, TRUE);
+ if (demands_attention)
+ client_hilite(self, TRUE);
+
+ if (max_vert && max_horz)
+ client_maximize(self, TRUE, 0);
+ else if (max_vert)
+ client_maximize(self, TRUE, 2);
+ else if (max_horz)
+ client_maximize(self, TRUE, 1);
+
+ /* fullscreen removes the ability to apply other states */
+ if (fullscreen)
+ client_fullscreen(self, TRUE);
+
+ /* make sure client_setup_decor_and_functions() is called at least once */
+ client_setup_decor_and_functions(self, FALSE);
+
+ /* if the window hasn't been configured yet, then do so now, in fact the
+ x,y,w,h may _not_ be the same as the area rect, which can end up
+ meaning that the client isn't properly moved/resized by the fullscreen
+ function
+ pho can cause this because it maps at size of the screen but not 0,0
+ so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
+ then fullscreen'ing makes it go to 0,0 which it thinks it already is at
+ cuz thats where the pre-fullscreen will be. however the actual area is
+ not, so this needs to be called even if we have fullscreened/maxed
+ */
+ self->area = oldarea;
+ client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
+
+ /* nothing to do for the other states:
+ skip_taskbar
+ skip_pager
+ modal
+ above
+ below
+ */
+}
+
+void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
+{
+ /* these should be the current values. this is for when you're not moving,
+ just resizing */
+ g_assert(*x == self->area.x);
+ g_assert(oldw == self->area.width);
+
+ /* horizontal */
+ switch (self->gravity) {
+ default:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ case StaticGravity:
+ case ForgetGravity:
+ break;
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ *x -= (neww - oldw) / 2;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ *x -= neww - oldw;
+ break;
+ }
+}
+
+void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
+{
+ /* these should be the current values. this is for when you're not moving,
+ just resizing */
+ g_assert(*y == self->area.y);
+ g_assert(oldh == self->area.height);
+
+ /* vertical */
+ switch (self->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ case StaticGravity:
+ case ForgetGravity:
+ break;
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ *y -= (newh - oldh) / 2;
+ break;
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ *y -= newh - oldh;
+ break;
+ }
+}
+
+void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
+ gint *logicalw, gint *logicalh,
+ gboolean user)
+{
+ Rect desired = {*x, *y, *w, *h};
+ frame_rect_to_frame(self->frame, &desired);
+
+ /* make the frame recalculate its dimensions n shit without changing
+ anything visible for real, this way the constraints below can work with
+ the updated frame dimensions. */
+ frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+
+ /* cap any X windows at the size of an unsigned short */
+ *w = MIN(*w,
+ (gint)G_MAXUSHORT
+ - self->frame->size.left - self->frame->size.right);
+ *h = MIN(*h,
+ (gint)G_MAXUSHORT
+ - self->frame->size.top - self->frame->size.bottom);
+
+ /* gets the frame's position */
+ frame_client_gravity(self->frame, x, y);
+
+ /* these positions are frame positions, not client positions */
+
+ /* set the size and position if fullscreen */
+ if (self->fullscreen) {
+ const Rect *a;
+ guint i;
+
+ i = screen_find_monitor(&desired);
+ a = screen_physical_area_monitor(i);
+
+ *x = a->x;
+ *y = a->y;
+ *w = a->width;
+ *h = a->height;
+
+ user = FALSE; /* ignore if the client can't be moved/resized when it
+ is fullscreening */
+ } else if (self->max_horz || self->max_vert) {
+ Rect *a;
+ guint i;
+
+ /* use all possible struts when maximizing to the full screen */
+ i = screen_find_monitor(&desired);
+ a = screen_area(self->desktop, i,
+ (self->max_horz && self->max_vert ? NULL : &desired));
+
+ /* set the size and position if maximized */
+ if (self->max_horz) {
+ *x = a->x;
+ *w = a->width - self->frame->size.left - self->frame->size.right;
+ }
+ if (self->max_vert) {
+ *y = a->y;
+ *h = a->height - self->frame->size.top - self->frame->size.bottom;
+ }
+
+ user = FALSE; /* ignore if the client can't be moved/resized when it
+ is maximizing */
+
+ g_slice_free(Rect, a);
+ }
+
+ /* gets the client's position */
+ frame_frame_gravity(self->frame, x, y);
+
+ /* work within the preferred sizes given by the window, these may have
+ changed rather than it's requested width and height, so always run
+ through this code */
+ {
+ gint basew, baseh, minw, minh;
+ gint incw, inch, maxw, maxh;
+ gfloat minratio, maxratio;
+
+ incw = self->size_inc.width;
+ inch = self->size_inc.height;
+ minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->min_ratio;
+ maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->max_ratio;
+
+ /* base size is substituted with min size if not specified */
+ if (self->base_size.width >= 0 || self->base_size.height >= 0) {
+ basew = self->base_size.width;
+ baseh = self->base_size.height;
+ } else {
+ basew = self->min_size.width;
+ baseh = self->min_size.height;
+ }
+ /* min size is substituted with base size if not specified */
+ if (self->min_size.width || self->min_size.height) {
+ minw = self->min_size.width;
+ minh = self->min_size.height;
+ } else {
+ minw = self->base_size.width;
+ minh = self->base_size.height;
+ }
+
+ /* This comment is no longer true */
+ /* if this is a user-requested resize, then check against min/max
+ sizes */
+
+ /* smaller than min size or bigger than max size? */
+ if (*w > self->max_size.width) *w = self->max_size.width;
+ if (*w < minw) *w = minw;
+ if (*h > self->max_size.height) *h = self->max_size.height;
+ if (*h < minh) *h = minh;
+
+ *w -= basew;
+ *h -= baseh;
+
+ /* the sizes to used for maximized */
+ maxw = *w;
+ maxh = *h;
+
+ /* keep to the increments */
+ *w /= incw;
+ *h /= inch;
+
+ /* you cannot resize to nothing */
+ if (basew + *w < 1) *w = 1 - basew;
+ if (baseh + *h < 1) *h = 1 - baseh;
+
+ /* save the logical size */
+ *logicalw = incw > 1 ? *w : *w + basew;
+ *logicalh = inch > 1 ? *h : *h + baseh;
+
+ *w *= incw;
+ *h *= inch;
+
+ /* if maximized/fs then don't use the size increments */
+ if (self->fullscreen || self->max_horz) *w = maxw;
+ if (self->fullscreen || self->max_vert) *h = maxh;
+
+ *w += basew;
+ *h += baseh;
+
+ /* adjust the height to match the width for the aspect ratios.
+ for this, min size is not substituted for base size ever. */
+ if (self->base_size.width >= 0 && self->base_size.height >= 0) {
+ *w -= self->base_size.width;
+ *h -= self->base_size.height;
+ }
+
+ if (minratio)
+ if (*h * minratio > *w) {
+ *h = (gint)(*w / minratio);
+
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
+ }
+ }
+ if (maxratio)
+ if (*h * maxratio < *w) {
+ *h = (gint)(*w / maxratio);
+
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
+ }
+ }
+
+ if (self->base_size.width >= 0 && self->base_size.height >= 0) {
+ *w += self->base_size.width;
+ *h += self->base_size.height;
+ }
+ }
+
+ /* these override the above states! if you cant move you can't move! */
+ if (user) {
+ if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
+ *x = self->area.x;
+ *y = self->area.y;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
+ *w = self->area.width;
+ *h = self->area.height;
+ }
+ }
+
+ g_assert(*w > 0);
+ g_assert(*h > 0);
+}
+
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
+ gboolean user, gboolean final, gboolean force_reply)
+{
+ Rect oldframe, oldclient;
+ gboolean send_resize_client;
+ gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
+ gboolean fmoved, fresized;
+ guint fdecor = self->frame->decorations;
+ gboolean fhorz = self->frame->max_horz;
+ gboolean fvert = self->frame->max_vert;
+ gint logicalw, logicalh;
+
+ /* find the new x, y, width, and height (and logical size) */
+ client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
+
+ /* set the logical size if things changed */
+ if (!(w == self->area.width && h == self->area.height))
+ SIZE_SET(self->logical_size, logicalw, logicalh);
+
+ /* figure out if we moved or resized or what */
+ moved = (x != self->area.x || y != self->area.y);
+ resized = (w != self->area.width || h != self->area.height);
+
+ oldframe = self->frame->area;
+ oldclient = self->area;
+ RECT_SET(self->area, x, y, w, h);
+
+ /* for app-requested resizes, always resize if 'resized' is true.
+ for user-requested ones, only resize if final is true, or when
+ resizing in redraw mode */
+ send_resize_client = ((!user && resized) ||
+ (user && (final ||
+ (resized && config_resize_redraw))));
+
+ /* if the client is enlarging, then resize the client before the frame */
+ if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
+ XMoveResizeWindow(obt_display, self->window,
+ self->frame->size.left, self->frame->size.top,
+ MAX(w, oldclient.width), MAX(h, oldclient.height));
+ frame_adjust_client_area(self->frame);
+ }
+
+ /* find the frame's dimensions and move/resize it */
+ fmoved = moved;
+ fresized = resized;
+
+ /* if decorations changed, then readjust everything for the frame */
+ if (self->decorations != fdecor ||
+ self->max_horz != fhorz || self->max_vert != fvert)
+ {
+ fmoved = fresized = TRUE;
+ }
+
+ /* adjust the frame */
+ if (fmoved || fresized) {
+ gulong ignore_start;
+ if (!user)
+ ignore_start = event_start_ignore_all_enters();
+
+ /* replay pending pointer event before move the window, in case it
+ would change what window gets the event */
+ mouse_replay_pointer();
+
+ frame_adjust_area(self->frame, fmoved, fresized, FALSE);
+
+ if (!user)
+ event_end_ignore_all_enters(ignore_start);
+ }
+
+ if (!user || final) {
+ gint oldrx = self->root_pos.x;
+ gint oldry = self->root_pos.y;
+ /* we have reset the client to 0 border width, so don't include
+ it in these coords */
+ POINT_SET(self->root_pos,
+ self->frame->area.x + self->frame->size.left -
+ self->border_width,
+ self->frame->area.y + self->frame->size.top -
+ self->border_width);
+ if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
+ rootmoved = TRUE;
+ }
+
+ /* This is kinda tricky and should not be changed.. let me explain!
+
+ When user = FALSE, then the request is coming from the application
+ itself, and we are more strict about when to send a synthetic
+ ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
+ in this case (or send one if force_reply is true)
+
+ When user = TRUE, then the request is coming from "us", like when we
+ maximize a window or something. In this case we are more lenient. We
+ used to follow the same rules as above, but _Java_ Swing can't handle
+ this. So just to appease Swing, when user = TRUE, we always send
+ a synthetic ConfigureNotify to give the window its root coordinates.
+ Lastly, if force_reply is TRUE, we always send a
+ ConfigureNotify, which is needed during a resize with XSYNCronization.
+ */
+ if ((!user && !resized && (rootmoved || force_reply)) ||
+ (user && ((!resized && force_reply) || (final && rootmoved))))
+ {
+ XEvent event;
+
+ event.type = ConfigureNotify;
+ event.xconfigure.display = obt_display;
+ event.xconfigure.event = self->window;
+ event.xconfigure.window = self->window;
+
+ ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
+ self->title, self->root_pos.x, self->root_pos.y, w, h);
+
+ /* root window real coords */
+ event.xconfigure.x = self->root_pos.x;
+ event.xconfigure.y = self->root_pos.y;
+ event.xconfigure.width = w;
+ event.xconfigure.height = h;
+ event.xconfigure.border_width = self->border_width;
+ event.xconfigure.above = None;
+ event.xconfigure.override_redirect = FALSE;
+ XSendEvent(event.xconfigure.display, event.xconfigure.window,
+ FALSE, StructureNotifyMask, &event);
+ }
+
+ /* if the client is shrinking, then resize the frame before the client.
+
+ both of these resize sections may run, because the top one only resizes
+ in the direction that is growing
+ */
+ if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
+ {
+ frame_adjust_client_area(self->frame);
+ XMoveResizeWindow(obt_display, self->window,
+ self->frame->size.left, self->frame->size.top, w, h);
+ }
+
+ XFlush(obt_display);
+
+ /* if it moved between monitors, then this can affect the stacking
+ layer of this window or others - for fullscreen windows.
+ also if it changed to/from oldschool fullscreen then its layer may
+ change
+
+ watch out tho, don't try change stacking stuff if the window is no
+ longer being managed !
+ */
+ if (self->managed &&
+ (screen_find_monitor(&self->frame->area) !=
+ screen_find_monitor(&oldframe) ||
+ (final && (client_is_oldfullscreen(self, &oldclient) !=
+ client_is_oldfullscreen(self, &self->area)))))
+ {
+ client_calc_layer(self);
+ }
+}
+
+void client_fullscreen(ObClient *self, gboolean fs)
+{
+ gint x, y, w, h;
+
+ if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
+ self->fullscreen == fs) return; /* already done */
+
+ self->fullscreen = fs;
+ client_change_state(self); /* change the state hints on the client */
+
+ if (fs) {
+ self->pre_fullscreen_area = self->area;
+ self->pre_fullscreen_max_horz = self->max_horz;
+ self->pre_fullscreen_max_vert = self->max_vert;
+
+ /* if the window is maximized, its area isn't all that meaningful.
+ save its premax area instead. */
+ if (self->max_horz) {
+ self->pre_fullscreen_area.x = self->pre_max_area.x;
+ self->pre_fullscreen_area.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_fullscreen_area.y = self->pre_max_area.y;
+ self->pre_fullscreen_area.height = self->pre_max_area.height;
+ }
+
+ /* these will help configure_full figure out where to fullscreen
+ the window */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+ } else {
+ g_assert(self->pre_fullscreen_area.width > 0 &&
+ self->pre_fullscreen_area.height > 0);
+
+ self->max_horz = self->pre_fullscreen_max_horz;
+ self->max_vert = self->pre_fullscreen_max_vert;
+ if (self->max_horz) {
+ self->pre_max_area.x = self->pre_fullscreen_area.x;
+ self->pre_max_area.width = self->pre_fullscreen_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_max_area.y = self->pre_fullscreen_area.y;
+ self->pre_max_area.height = self->pre_fullscreen_area.height;
+ }
+
+ x = self->pre_fullscreen_area.x;
+ y = self->pre_fullscreen_area.y;
+ w = self->pre_fullscreen_area.width;
+ h = self->pre_fullscreen_area.height;
+ RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
+ }
+
+ ob_debug("Window %s going fullscreen (%d)",
+ self->title, self->fullscreen);
+
+ if (fs) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
+ client_setup_decor_and_functions(self, FALSE);
+ client_move_resize(self, x, y, w, h);
+
+ /* and adjust our layer/stacking. do this after resizing the window,
+ and applying decorations, because windows which fill the screen are
+ considered "fullscreen" and it affects their layer */
+ client_calc_layer(self);
+
+ if (fs) {
+ /* try focus us when we go into fullscreen mode */
+ client_focus(self);
+ }
+}
+
+static void client_iconify_recursive(ObClient *self,
+ gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
+{
+ GSList *it;
+ gboolean changed = FALSE;
+
+ if (self->iconic != iconic) {
+ ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
+ self->window);
+
+ if (iconic) {
+ /* don't let non-normal windows iconify along with their parents
+ or whatever */
+ if (client_normal(self)) {
+ self->iconic = iconic;
+
+ /* update the focus lists.. iconic windows go to the bottom of
+ the list. this will also call focus_cycle_addremove(). */
+ focus_order_to_bottom(self);
+
+ changed = TRUE;
+ }
+ } else {
+ self->iconic = iconic;
+
+ if (curdesk && self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ client_set_desktop(self, screen_desktop, FALSE, FALSE);
+
+ /* this puts it after the current focused window, this will
+ also cause focus_cycle_addremove() to be called for the
+ client */
+ focus_order_like_new(self);
+
+ changed = TRUE;
+ }
+ }
+
+ if (changed) {
+ client_change_state(self);
+ if (config_animate_iconify && !hide_animation)
+ frame_begin_iconify_animation(self->frame, iconic);
+ /* do this after starting the animation so it doesn't flash */
+ client_showhide(self);
+ }
+
+ /* iconify all direct transients, and deiconify all transients
+ (non-direct too) */
+ for (it = self->transients; it; it = g_slist_next(it))
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data) || !iconic)
+ client_iconify_recursive(it->data, iconic, curdesk,
+ hide_animation);
+}
+
+void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
+{
+ if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
+ /* move up the transient chain as far as possible first */
+ self = client_search_top_direct_parent(self);
+ client_iconify_recursive(self, iconic, curdesk, hide_animation);
+ }
+}
+
+void client_maximize(ObClient *self, gboolean max, gint dir)
+{
+ gint x, y, w, h;
+
+ g_assert(dir == 0 || dir == 1 || dir == 2);
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
+
+ /* check if already done */
+ if (max) {
+ if (dir == 0 && self->max_horz && self->max_vert) return;
+ if (dir == 1 && self->max_horz) return;
+ if (dir == 2 && self->max_vert) return;
+ } else {
+ if (dir == 0 && !self->max_horz && !self->max_vert) return;
+ if (dir == 1 && !self->max_horz) return;
+ if (dir == 2 && !self->max_vert) return;
+ }
+
+ /* these will help configure_full figure out which screen to fill with
+ the window */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+
+ if (max) {
+ if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
+ RECT_SET(self->pre_max_area,
+ self->area.x, self->pre_max_area.y,
+ self->area.width, self->pre_max_area.height);
+ }
+ if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
+ RECT_SET(self->pre_max_area,
+ self->pre_max_area.x, self->area.y,
+ self->pre_max_area.width, self->area.height);
+ }
+ } else {
+ if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
+ g_assert(self->pre_max_area.width > 0);
+
+ x = self->pre_max_area.x;
+ w = self->pre_max_area.width;
+
+ RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
+ 0, self->pre_max_area.height);
+ }
+ if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
+ g_assert(self->pre_max_area.height > 0);
+
+ y = self->pre_max_area.y;
+ h = self->pre_max_area.height;
+
+ RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
+ self->pre_max_area.width, 0);
+ }
+ }
+
+ if (dir == 0 || dir == 1) /* horz */
+ self->max_horz = max;
+ if (dir == 0 || dir == 2) /* vert */
+ self->max_vert = max;
+
+ if (max) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
+ client_change_state(self); /* change the state hints on the client */
+
+ client_setup_decor_and_functions(self, FALSE);
+ client_move_resize(self, x, y, w, h);
+}
+
+void client_shade(ObClient *self, gboolean shade)
+{
+ if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
+ shade) || /* can't shade */
+ self->shaded == shade) return; /* already done */
+
+ self->shaded = shade;
+ client_change_state(self);
+ client_change_wm_state(self); /* the window is being hidden/shown */
+ /* resize the frame to just the titlebar */
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+}
+
+static void client_ping_event(ObClient *self, gboolean dead)
+{
+ if (self->not_responding != dead) {
+ self->not_responding = dead;
+ client_update_title(self);
+
+ if (dead)
+ /* the client isn't responding, so ask to kill it */
+ client_prompt_kill(self);
+ else {
+ /* it came back to life ! */
+
+ if (self->kill_prompt) {
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+ }
+
+ self->kill_level = 0;
+ }
+ }
+}
+
+void client_close(ObClient *self)
+{
+ if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
+
+ /* if closing an internal obprompt, that is just cancelling it */
+ if (self->prompt) {
+ prompt_cancel(self->prompt);
+ return;
+ }
+
+ /* in the case that the client provides no means to requesting that it
+ close, we just kill it */
+ if (!self->delete_window)
+ /* don't use client_kill(), we should only kill based on PID in
+ response to a lack of PING replies */
+ XKillClient(obt_display, self->window);
+ else {
+ /* request the client to close with WM_DELETE_WINDOW */
+ OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
+ OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
+ 0, 0, 0, NoEventMask);
+
+ /* we're trying to close the window, so see if it is responding. if it
+ is not, then we will let them kill the window */
+ if (self->ping)
+ ping_start(self, client_ping_event);
+
+ /* if we already know the window isn't responding (maybe they clicked
+ no in the kill dialog but it hasn't come back to life), then show
+ the kill dialog */
+ if (self->not_responding)
+ client_prompt_kill(self);
+ }
+}
+
+#define OB_KILL_RESULT_NO 0
+#define OB_KILL_RESULT_YES 1
+
+static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
+{
+ ObClient *self = data;
+
+ if (result == OB_KILL_RESULT_YES)
+ client_kill(self);
+ return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+ ObClient *self = data;
+
+ g_assert(p == self->kill_prompt);
+
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+}
+
+static void client_prompt_kill(ObClient *self)
+{
+ /* check if we're already prompting */
+ if (!self->kill_prompt) {
+ ObPromptAnswer answers[] = {
+ { 0, OB_KILL_RESULT_NO },
+ { 0, OB_KILL_RESULT_YES }
+ };
+ gchar *m;
+ const gchar *y, *title;
+
+ title = self->original_title;
+ if (title[0] == '\0') {
+ /* empty string, so use its parent */
+ ObClient *p = client_search_top_direct_parent(self);
+ if (p) title = p->original_title;
+ }
+
+ if (client_on_localhost(self)) {
+ const gchar *sig;
+
+ if (self->kill_level == 0)
+ sig = "terminate";
+ else
+ sig = "kill";
+
+ m = g_strdup_printf
+ (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
+ title, sig);
+ y = _("End Process");
+ }
+ else {
+ m = g_strdup_printf
+ (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
+ title);
+ y = _("Disconnect");
+ }
+ /* set the dialog buttons' text */
+ answers[0].text = _("Cancel"); /* "no" */
+ answers[1].text = y; /* "yes" */
+
+ self->kill_prompt = prompt_new(m, NULL, answers,
+ sizeof(answers)/sizeof(answers[0]),
+ OB_KILL_RESULT_NO, /* default = no */
+ OB_KILL_RESULT_NO, /* cancel = no */
+ client_kill_requested,
+ client_kill_cleanup,
+ self);
+ g_free(m);
+ }
+
+ prompt_show(self->kill_prompt, self, TRUE);
+}
+
+void client_kill(ObClient *self)
+{
+ /* don't kill our own windows */
+ if (self->prompt) return;
+
+ if (client_on_localhost(self) && self->pid) {
+ /* running on the local host */
+ if (self->kill_level == 0) {
+ ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
+ self->window, self->pid);
+ kill(self->pid, SIGTERM);
+ ++self->kill_level;
+
+ /* show that we're trying to kill it */
+ client_update_title(self);
+ }
+ else {
+ ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
+ self->window, self->pid);
+ kill(self->pid, SIGKILL); /* kill -9 */
+ }
+ }
+ else {
+ /* running on a remote host */
+ XKillClient(obt_display, self->window);
+ }
+}
+
+void client_hilite(ObClient *self, gboolean hilite)
+{
+ if (self->demands_attention == hilite)
+ return; /* no change */
+
+ /* don't allow focused windows to hilite */
+ self->demands_attention = hilite && !client_focused(self);
+ if (self->frame != NULL) { /* if we're mapping, just set the state */
+ if (self->demands_attention) {
+ frame_flash_start(self->frame);
+
+ /* if the window is on another desktop then raise it and make it
+ the most recently used window */
+ if (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ {
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ focus_order_to_top(self);
+ }
+ }
+ else
+ frame_flash_stop(self->frame);
+ client_change_state(self);
+ }
+}
+
+static void client_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise)
+{
+ guint old;
+ GSList *it;
+
+ if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
+
+ ob_debug("Setting desktop %u", target+1);
+
+ g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
+
+ old = self->desktop;
+ self->desktop = target;
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
+ /* the frame can display the current desktop state */
+ frame_adjust_state(self->frame);
+ /* 'move' the window to the new desktop */
+ if (!donthide)
+ client_hide(self);
+ client_show(self);
+ /* raise if it was not already on the desktop */
+ if (old != DESKTOP_ALL && !dontraise)
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+ else
+ /* the new desktop's geometry may be different, so we may need to
+ resize, for example if we are maximized */
+ client_reconfigure(self, FALSE);
+
+ focus_cycle_addremove(self, FALSE);
+ }
+
+ /* move all transients */
+ for (it = self->transients; it; it = g_slist_next(it))
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data))
+ client_set_desktop_recursive(it->data, target,
+ donthide, dontraise);
+}
+
+void client_set_desktop(ObClient *self, guint target,
+ gboolean donthide, gboolean dontraise)
+{
+ self = client_search_top_direct_parent(self);
+ client_set_desktop_recursive(self, target, donthide, dontraise);
+
+ focus_cycle_addremove(NULL, TRUE);
+}
+
+gboolean client_is_direct_child(ObClient *parent, ObClient *child)
+{
+ while (child != parent && (child = client_direct_parent(child)));
+ return child == parent;
+}
+
+ObClient *client_search_modal_child(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret;
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if ((ret = client_search_modal_child(c))) return ret;
+ if (c->modal) return c;
+ }
+ return NULL;
+}
+
+struct ObClientFindDestroyUnmap {
+ Window window;
+ gint ignore_unmaps;
+};
+
+static gboolean find_destroy_unmap(XEvent *e, gpointer data)
+{
+ struct ObClientFindDestroyUnmap *find = data;
+ if (e->type == DestroyNotify)
+ return e->xdestroywindow.window == find->window;
+ if (e->type == UnmapNotify && e->xunmap.window == find->window)
+ /* ignore the first $find->ignore_unmaps$ many unmap events */
+ return --find->ignore_unmaps < 0;
+ return FALSE;
+}
+
+gboolean client_validate(ObClient *self)
+{
+ struct ObClientFindDestroyUnmap find;
+
+ XSync(obt_display, FALSE); /* get all events on the server */
+
+ find.window = self->window;
+ find.ignore_unmaps = self->ignore_unmaps;
+ if (xqueue_exists_local(find_destroy_unmap, &find))
+ return FALSE;
+
+ return TRUE;
+}
+
+void client_set_wm_state(ObClient *self, glong state)
+{
+ if (state == self->wmstate) return; /* no change */
+
+ switch (state) {
+ case IconicState:
+ client_iconify(self, TRUE, TRUE, FALSE);
+ break;
+ case NormalState:
+ client_iconify(self, FALSE, TRUE, FALSE);
+ break;
+ }
+}
+
+void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
+{
+ gboolean shaded = self->shaded;
+ gboolean fullscreen = self->fullscreen;
+ gboolean undecorated = self->undecorated;
+ gboolean max_horz = self->max_horz;
+ gboolean max_vert = self->max_vert;
+ gboolean modal = self->modal;
+ gboolean iconic = self->iconic;
+ gboolean demands_attention = self->demands_attention;
+ gboolean above = self->above;
+ gboolean below = self->below;
+ gint i;
+ gboolean value;
+
+ if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
+ action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
+ action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
+ /* an invalid action was passed to the client message, ignore it */
+ return;
+
+ for (i = 0; i < 2; ++i) {
+ Atom state = i == 0 ? data1 : data2;
+
+ if (!state) continue;
+
+ /* if toggling, then pick whether we're adding or removing */
+ if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
+ if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+ value = modal;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+ value = self->max_vert;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+ value = self->max_horz;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+ value = shaded;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
+ value = self->skip_taskbar;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
+ value = self->skip_pager;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
+ value = self->iconic;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
+ value = fullscreen;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+ value = self->above;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+ value = self->below;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
+ value = self->demands_attention;
+ else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+ value = undecorated;
+ else
+ g_assert_not_reached();
+ action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+ OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ }
+
+ value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
+ modal = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
+ max_vert = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
+ max_horz = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
+ shaded = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
+ self->skip_taskbar = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
+ self->skip_pager = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
+ iconic = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
+ fullscreen = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+ above = value;
+ /* only unset below when setting above, otherwise you can't get to
+ the normal layer */
+ if (value)
+ below = FALSE;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+ /* and vice versa */
+ if (value)
+ above = FALSE;
+ below = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
+ demands_attention = value;
+ } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
+ undecorated = value;
+ }
+ }
+
+ if (max_horz != self->max_horz || max_vert != self->max_vert) {
+ if (max_horz != self->max_horz && max_vert != self->max_vert) {
+ /* toggling both */
+ if (max_horz == max_vert) { /* both going the same way */
+ client_maximize(self, max_horz, 0);
+ } else {
+ client_maximize(self, max_horz, 1);
+ client_maximize(self, max_vert, 2);
+ }
+ } else {
+ /* toggling one */
+ if (max_horz != self->max_horz)
+ client_maximize(self, max_horz, 1);
+ else
+ client_maximize(self, max_vert, 2);
+ }
+ }
+ /* change fullscreen state before shading, as it will affect if the window
+ can shade or not */
+ if (fullscreen != self->fullscreen)
+ client_fullscreen(self, fullscreen);
+ if (shaded != self->shaded)
+ client_shade(self, shaded);
+ if (undecorated != self->undecorated)
+ client_set_undecorated(self, undecorated);
+ if (above != self->above || below != self->below) {
+ self->above = above;
+ self->below = below;
+ client_calc_layer(self);
+ }
+
+ if (modal != self->modal) {
+ self->modal = modal;
+ /* when a window changes modality, then its stacking order with its
+ transients needs to change */
+ stacking_raise(CLIENT_AS_WINDOW(self));
+
+ /* it also may get focused. if something is focused that shouldn't
+ be focused anymore, then move the focus */
+ if (focus_client && client_focus_target(focus_client) != focus_client)
+ client_focus(focus_client);
+ }
+
+ if (iconic != self->iconic)
+ client_iconify(self, iconic, FALSE, FALSE);
+
+ if (demands_attention != self->demands_attention)
+ client_hilite(self, demands_attention);
+
+ client_change_state(self); /* change the hint to reflect these changes */
+
+ focus_cycle_addremove(self, TRUE);
+}
+
+ObClient *client_focus_target(ObClient *self)
+{
+ ObClient *child = NULL;
+
+ child = client_search_modal_child(self);
+ if (child) return child;
+ return self;
+}
+
+gboolean client_can_focus(ObClient *self)
+{
+ /* choose the correct target */
+ self = client_focus_target(self);
+
+ if (!self->frame->visible)
+ return FALSE;
+
+ if (!(self->can_focus || self->focus_notify))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean client_focus(ObClient *self)
+{
+ if (!client_validate(self)) return FALSE;
+
+ /* we might not focus this window, so if we have modal children which would
+ be focused instead, bring them to this desktop */
+ client_bring_modal_windows(self);
+
+ /* choose the correct target */
+ self = client_focus_target(self);
+
+ if (!client_can_focus(self)) {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Client %s can't be focused", self->title);
+ return FALSE;
+ }
+
+ /* if we have helper windows they should be there with the window */
+ client_bring_helper_windows(self);
+
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focusing client \"%s\" (0x%x) at time %u",
+ self->title, self->window, event_time());
+
+ /* if using focus_delay, stop the timer now so that focus doesn't
+ go moving on us */
+ event_halt_focus_delay();
+
+ obt_display_ignore_errors(TRUE);
+
+ if (self->can_focus) {
+ /* This can cause a BadMatch error with CurrentTime, or if an app
+ passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
+ XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
+ event_time());
+ }
+
+ if (self->focus_notify) {
+ XEvent ce;
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
+ ce.xclient.display = obt_display;
+ ce.xclient.window = self->window;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
+ ce.xclient.data.l[1] = event_time();
+ ce.xclient.data.l[2] = 0l;
+ ce.xclient.data.l[3] = 0l;
+ ce.xclient.data.l[4] = 0l;
+ XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
+ }
+
+ obt_display_ignore_errors(FALSE);
+
+ ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
+ obt_display_error_occured);
+ return !obt_display_error_occured;
+}
+
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade)
+{
+ if (client_normal(self) && screen_showing_desktop())
+ screen_show_desktop(FALSE, self);
+ if (self->iconic)
+ client_iconify(self, FALSE, here, FALSE);
+ if (self->desktop != DESKTOP_ALL &&
+ self->desktop != screen_desktop)
+ {
+ if (here)
+ client_set_desktop(self, screen_desktop, FALSE, TRUE);
+ else
+ screen_set_desktop(self->desktop, FALSE);
+ } else if (!self->frame->visible)
+ /* if its not visible for other reasons, then don't mess
+ with it */
+ return;
+ if (self->shaded && unshade)
+ client_shade(self, FALSE);
+ if (raise)
+ stacking_raise(CLIENT_AS_WINDOW(self));
+
+ client_focus(self);
+}
+
+/* this function exists to map to the net_active_window message in the ewmh */
+void client_activate(ObClient *self, gboolean desktop,
+ gboolean here, gboolean raise,
+ gboolean unshade, gboolean user)
+{
+ self = client_focus_target(self);
+
+ if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
+ client_present(self, here, raise, unshade);
+ else
+ client_hilite(self, TRUE);
+}
+
+static void client_bring_windows_recursive(ObClient *self,
+ guint desktop,
+ gboolean helpers,
+ gboolean modals,
+ gboolean iconic)
+{
+ GSList *it;
+
+ for (it = self->transients; it; it = g_slist_next(it))
+ client_bring_windows_recursive(it->data, desktop,
+ helpers, modals, iconic);
+
+ if (((helpers && client_helper(self)) ||
+ (modals && self->modal)) &&
+ (!screen_compare_desktops(self->desktop, desktop) ||
+ (iconic && self->iconic)))
+ {
+ if (iconic && self->iconic)
+ client_iconify(self, FALSE, TRUE, FALSE);
+ else
+ client_set_desktop(self, desktop, FALSE, FALSE);
+ }
+}
+
+void client_bring_helper_windows(ObClient *self)
+{
+ client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
+}
+
+void client_bring_modal_windows(ObClient *self)
+{
+ client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
+}
+
+gboolean client_focused(ObClient *self)
+{
+ return self == focus_client;
+}
+
+RrImage* client_icon(ObClient *self)
+{
+ RrImage *ret = NULL;
+
+ if (self->icon_set)
+ ret = self->icon_set;
+ else if (self->parents) {
+ GSList *it;
+ for (it = self->parents; it && !ret; it = g_slist_next(it))
+ ret = client_icon(it->data);
+ }
+ if (!ret)
+ ret = client_default_icon;
+ return ret;
+}
+
+void client_set_layer(ObClient *self, gint layer)
+{
+ if (layer < 0) {
+ self->below = TRUE;
+ self->above = FALSE;
+ } else if (layer == 0) {
+ self->below = self->above = FALSE;
+ } else {
+ self->below = FALSE;
+ self->above = TRUE;
+ }
+ client_calc_layer(self);
+ client_change_state(self); /* reflect this in the state hints */
+}
+
+void client_set_undecorated(ObClient *self, gboolean undecorated)
+{
+ if (self->undecorated != undecorated &&
+ /* don't let it undecorate if the function is missing, but let
+ it redecorate */
+ (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
+ {
+ self->undecorated = undecorated;
+ client_setup_decor_and_functions(self, TRUE);
+ client_change_state(self); /* reflect this in the state hints */
+ }
+}
+
+guint client_monitor(ObClient *self)
+{
+ return screen_find_monitor(&self->frame->area);
+}
+
+ObClient *client_direct_parent(ObClient *self)
+{
+ if (!self->parents) return NULL;
+ if (self->transient_for_group) return NULL;
+ return self->parents->data;
+}
+
+ObClient *client_search_top_direct_parent(ObClient *self)
+{
+ ObClient *p;
+ while ((p = client_direct_parent(self))) self = p;
+ return self;
+}
+
+static GSList *client_search_all_top_parents_internal(ObClient *self,
+ gboolean bylayer,
+ ObStackingLayer layer)
+{
+ GSList *ret;
+ ObClient *p;
+
+ /* move up the direct transient chain as far as possible */
+ while ((p = client_direct_parent(self)) &&
+ (!bylayer || p->layer == layer))
+ self = p;
+
+ if (!self->parents)
+ ret = g_slist_prepend(NULL, self);
+ else
+ ret = g_slist_copy(self->parents);
+
+ return ret;
+}
+
+GSList *client_search_all_top_parents(ObClient *self)
+{
+ return client_search_all_top_parents_internal(self, FALSE, 0);
+}
+
+GSList *client_search_all_top_parents_layer(ObClient *self)
+{
+ return client_search_all_top_parents_internal(self, TRUE, self->layer);
+}
+
+ObClient *client_search_focus_parent(ObClient *self)
+{
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (client_focused(it->data)) return it->data;
+
+ return NULL;
+}
+
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret = NULL;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ if (client_focused(it->data))
+ ret = it->data;
+ else
+ ret = client_search_focus_parent_full(it->data);
+ if (ret) break;
+ }
+ return ret;
+}
+
+ObClient *client_search_parent(ObClient *self, ObClient *search)
+{
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (it->data == search) return search;
+
+ return NULL;
+}
+
+ObClient *client_search_transient(ObClient *self, ObClient *search)
+{
+ GSList *sit;
+
+ for (sit = self->transients; sit; sit = g_slist_next(sit)) {
+ if (sit->data == search)
+ return search;
+ if (client_search_transient(sit->data, search))
+ return search;
+ }
+ return NULL;
+}
+
+static void detect_edge(Rect area, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ gint edge_start, edge_size, head, tail;
+ gboolean skip_head = FALSE, skip_tail = FALSE;
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ edge_start = area.x;
+ edge_size = area.width;
+ break;
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_WEST:
+ edge_start = area.y;
+ edge_size = area.height;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* do we collide with this window? */
+ if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
+ edge_start, edge_size))
+ return;
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ head = RECT_BOTTOM(area);
+ tail = RECT_TOP(area);
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_TOP(area);
+ tail = RECT_BOTTOM(area);
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_RIGHT(area);
+ tail = RECT_LEFT(area);
+ break;
+ case OB_DIRECTION_EAST:
+ head = RECT_LEFT(area);
+ tail = RECT_RIGHT(area);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_WEST:
+ /* check if our window is past the head of this window */
+ if (my_head <= head + 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head + my_size - 1 <= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head + (*near_edge ? 0 : my_size) <= *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail - (!*near_edge ? 0 : my_size) <= *dest)
+ skip_tail = TRUE;
+ break;
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_EAST:
+ /* check if our window is past the head of this window */
+ if (my_head >= head - 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head - my_size + 1 >= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head - (*near_edge ? 0 : my_size) >= *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail + (!*near_edge ? 0 : my_size) >= *dest)
+ skip_tail = TRUE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ob_debug("my head %d size %d", my_head, my_size);
+ ob_debug("head %d tail %d dest %d", head, tail, *dest);
+ if (!skip_head) {
+ ob_debug("using near edge %d", head);
+ *dest = head;
+ *near_edge = TRUE;
+ }
+ else if (!skip_tail) {
+ ob_debug("using far edge %d", tail);
+ *dest = tail;
+ *near_edge = FALSE;
+ }
+}
+
+void client_find_edge_directional(ObClient *self, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ GList *it;
+ Rect *a;
+ Rect dock_area;
+ gint edge;
+ guint i;
+
+ a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
+ &self->frame->area);
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ edge = RECT_TOP(*a) - 1;
+ break;
+ case OB_DIRECTION_SOUTH:
+ edge = RECT_BOTTOM(*a) + 1;
+ break;
+ case OB_DIRECTION_EAST:
+ edge = RECT_RIGHT(*a) + 1;
+ break;
+ case OB_DIRECTION_WEST:
+ edge = RECT_LEFT(*a) - 1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ /* default to the far edge, then narrow it down */
+ *dest = edge;
+ *near_edge = TRUE;
+
+ /* search for edges of monitors */
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *area = screen_area(self->desktop, i, NULL);
+ detect_edge(*area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ g_slice_free(Rect, area);
+ }
+
+ /* search for edges of clients */
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *cur = it->data;
+
+ /* skip windows to not bump into */
+ if (cur == self)
+ continue;
+ if (cur->iconic)
+ continue;
+ if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+ cur->desktop != screen_desktop)
+ continue;
+
+ ob_debug("trying window %s", cur->title);
+
+ detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ }
+ dock_get_area(&dock_area);
+ detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+
+ g_slice_free(Rect, a);
+}
+
+void client_find_move_directional(ObClient *self, ObDirection dir,
+ gint *x, gint *y)
+{
+ gint head, size;
+ gint e, e_start, e_size;
+ gboolean near;
+
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ head = RECT_RIGHT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ client_find_edge_directional(self, dir, head, size,
+ e_start, e_size, &e, &near);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ if (near) e -= self->frame->area.width;
+ else e++;
+ *x = e;
+ break;
+ case OB_DIRECTION_WEST:
+ if (near) e++;
+ else e -= self->frame->area.width;
+ *x = e;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (near) e++;
+ else e -= self->frame->area.height;
+ *y = e;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (near) e -= self->frame->area.height;
+ else e++;
+ *y = e;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+}
+
+void client_find_resize_directional(ObClient *self,
+ ObDirection side,
+ ObClientDirectionalResizeType resize_type,
+ gint *x, gint *y, gint *w, gint *h)
+{
+ gint head;
+ gint e, e_start, e_size, delta;
+ gboolean near;
+ ObDirection dir;
+
+ gboolean grow;
+ switch (resize_type) {
+ case CLIENT_RESIZE_GROW:
+ grow = TRUE;
+ break;
+ case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE:
+ grow = TRUE;
+ break;
+ case CLIENT_RESIZE_SHRINK:
+ grow = FALSE;
+ break;
+ }
+
+ switch (side) {
+ case OB_DIRECTION_EAST:
+ head = RECT_RIGHT(self->frame->area);
+ switch (resize_type) {
+ case CLIENT_RESIZE_GROW:
+ head += self->size_inc.width - 1;
+ break;
+ case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE:
+ head -= 1;
+ break;
+ case CLIENT_RESIZE_SHRINK:
+ break;
+ }
+
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area);
+ switch (resize_type) {
+ case CLIENT_RESIZE_GROW:
+ head -= self->size_inc.width - 1;
+ break;
+ case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE:
+ head += 1;
+ break;
+ case CLIENT_RESIZE_SHRINK:
+ break;
+ }
+
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area);
+ switch (resize_type) {
+ case CLIENT_RESIZE_GROW:
+ head -= self->size_inc.height - 1;
+ break;
+ case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE:
+ head += 1;
+ break;
+ case CLIENT_RESIZE_SHRINK:
+ break;
+ }
+
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area);
+ switch (resize_type) {
+ case CLIENT_RESIZE_GROW:
+ head += self->size_inc.height - 1;
+ break;
+ case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE:
+ head -= 1;
+ break;
+ case CLIENT_RESIZE_SHRINK:
+ break;
+ }
+
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ob_debug("head %d dir %d", head, dir);
+ client_find_edge_directional(self, dir, head, 1,
+ e_start, e_size, &e, &near);
+ ob_debug("edge %d", e);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ *w = self->frame->area.width;
+ *h = self->frame->area.height;
+ switch (side) {
+ case OB_DIRECTION_EAST:
+ if (grow == near) --e;
+ delta = e - RECT_RIGHT(self->frame->area);
+ *w += delta;
+ break;
+ case OB_DIRECTION_WEST:
+ if (grow == near) ++e;
+ delta = RECT_LEFT(self->frame->area) - e;
+ *x -= delta;
+ *w += delta;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (grow == near) ++e;
+ delta = RECT_TOP(self->frame->area) - e;
+ *y -= delta;
+ *h += delta;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (grow == near) --e;
+ delta = e - RECT_BOTTOM(self->frame->area);
+ *h += delta;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+ *w -= self->frame->size.left + self->frame->size.right;
+ *h -= self->frame->size.top + self->frame->size.bottom;
+}
+
+ObClient* client_under_pointer(void)
+{
+ gint x, y;
+ GList *it;
+ ObClient *ret = NULL;
+
+ if (screen_pointer_pos(&x, &y)) {
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = WINDOW_AS_CLIENT(it->data);
+ if (c->frame->visible &&
+ /* check the desktop, this is done during desktop
+ switching and windows are shown/hidden status is not
+ reliable */
+ (c->desktop == screen_desktop ||
+ c->desktop == DESKTOP_ALL) &&
+ /* ignore all animating windows */
+ !frame_iconify_animating(c->frame) &&
+ RECT_CONTAINS(c->frame->area, x, y))
+ {
+ ret = c;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+gboolean client_has_group_siblings(ObClient *self)
+{
+ return self->group && self->group->members->next;
+}
+
+gboolean client_has_relative(ObClient *self)
+{
+ return client_has_parent(self) ||
+ client_has_group_siblings(self) ||
+ client_has_children(self);
+}
+
+/*! Returns TRUE if the client is running on the same machine as Openbox */
+gboolean client_on_localhost(ObClient *self)
+{
+ return self->client_machine == NULL;
+}
diff -Naur a/openbox/frame.c b/openbox/frame.c
--- a/openbox/frame.c 2014-11-05 16:19:42.000000000 +0100
+++ b/openbox/frame.c 2023-04-07 20:17:27.194119423 +0200
@@ -386,12 +386,6 @@
if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
self->size.top += ob_rr_theme->title_height + self->bwidth;
- else if (self->max_horz && self->max_vert) {
- /* A maximized and undecorated window needs a border on the
- top of the window to let the user still undecorate/unmaximize the
- window via the client menu. */
- self->size.top += self->bwidth;
- }
if (self->decorations & OB_FRAME_DECOR_HANDLE &&
ob_rr_theme->handle_height > 0)
diff -Naur a/openbox/frame.c.orig b/openbox/frame.c.orig
--- a/openbox/frame.c.orig 1970-01-01 01:00:00.000000000 +0100
+++ b/openbox/frame.c.orig 2014-11-05 16:19:42.000000000 +0100
@@ -0,0 +1,1877 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ frame.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "frame.h"
+#include "client.h"
+#include "openbox.h"
+#include "grab.h"
+#include "debug.h"
+#include "config.h"
+#include "framerender.h"
+#include "focus_cycle.h"
+#include "focus_cycle_indicator.h"
+#include "moveresize.h"
+#include "screen.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+
+#define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
+ ButtonPressMask | ButtonReleaseMask | \
+ SubstructureRedirectMask | FocusChangeMask)
+#define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
+ ButtonMotionMask | PointerMotionMask | \
+ EnterWindowMask | LeaveWindowMask)
+
+#define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
+#define FRAME_ANIMATE_ICONIFY_STEP_TIME (1000 / 60) /* 60 Hz */
+
+#define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
+
+static void flash_done(gpointer data);
+static gboolean flash_timeout(gpointer data);
+
+static void layout_title(ObFrame *self);
+static void set_theme_statics(ObFrame *self);
+static void free_theme_statics(ObFrame *self);
+static gboolean frame_animate_iconify(gpointer self);
+static void frame_adjust_cursors(ObFrame *self);
+
+static Window createWindow(Window parent, Visual *visual,
+ gulong mask, XSetWindowAttributes *attrib)
+{
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
+ (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
+ (visual ? visual : RrVisual(ob_rr_inst)),
+ mask, attrib);
+
+}
+
+static Visual *check_32bit_client(ObClient *c)
+{
+ XWindowAttributes wattrib;
+ Status ret;
+
+ /* we're already running at 32 bit depth, yay. we don't need to use their
+ visual */
+ if (RrDepth(ob_rr_inst) == 32)
+ return NULL;
+
+ ret = XGetWindowAttributes(obt_display, c->window, &wattrib);
+ g_assert(ret != BadDrawable);
+ g_assert(ret != BadWindow);
+
+ if (wattrib.depth == 32)
+ return wattrib.visual;
+ return NULL;
+}
+
+ObFrame *frame_new(ObClient *client)
+{
+ XSetWindowAttributes attrib;
+ gulong mask;
+ ObFrame *self;
+ Visual *visual;
+
+ self = g_slice_new0(ObFrame);
+ self->client = client;
+
+ visual = check_32bit_client(client);
+
+ /* create the non-visible decor windows */
+
+ mask = 0;
+ if (visual) {
+ /* client has a 32-bit visual */
+ mask = CWColormap | CWBackPixel | CWBorderPixel;
+ /* create a colormap with the visual */
+ self->colormap = attrib.colormap =
+ XCreateColormap(obt_display, obt_root(ob_screen),
+ visual, AllocNone);
+ attrib.background_pixel = BlackPixel(obt_display, ob_screen);
+ attrib.border_pixel = BlackPixel(obt_display, ob_screen);
+ }
+ self->window = createWindow(obt_root(ob_screen), visual,
+ mask, &attrib);
+
+ /* create the visible decor windows */
+
+ mask = 0;
+ if (visual) {
+ /* client has a 32-bit visual */
+ mask = CWColormap | CWBackPixel | CWBorderPixel;
+ attrib.colormap = RrColormap(ob_rr_inst);
+ }
+
+ self->backback = createWindow(self->window, NULL, mask, &attrib);
+ self->backfront = createWindow(self->backback, NULL, mask, &attrib);
+
+ mask |= CWEventMask;
+ attrib.event_mask = ELEMENT_EVENTMASK;
+ self->innerleft = createWindow(self->window, NULL, mask, &attrib);
+ self->innertop = createWindow(self->window, NULL, mask, &attrib);
+ self->innerright = createWindow(self->window, NULL, mask, &attrib);
+ self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib);
+ self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib);
+ self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib);
+ self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib);
+
+ self->title = createWindow(self->window, NULL, mask, &attrib);
+ self->titleleft = createWindow(self->window, NULL, mask, &attrib);
+ self->titletop = createWindow(self->window, NULL, mask, &attrib);
+ self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
+ self->titletopright = createWindow(self->window, NULL, mask, &attrib);
+ self->titleright = createWindow(self->window, NULL, mask, &attrib);
+ self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->topresize = createWindow(self->title, NULL, mask, &attrib);
+ self->tltresize = createWindow(self->title, NULL, mask, &attrib);
+ self->tllresize = createWindow(self->title, NULL, mask, &attrib);
+ self->trtresize = createWindow(self->title, NULL, mask, &attrib);
+ self->trrresize = createWindow(self->title, NULL, mask, &attrib);
+
+ self->left = createWindow(self->window, NULL, mask, &attrib);
+ self->right = createWindow(self->window, NULL, mask, &attrib);
+
+ self->label = createWindow(self->title, NULL, mask, &attrib);
+ self->max = createWindow(self->title, NULL, mask, &attrib);
+ self->close = createWindow(self->title, NULL, mask, &attrib);
+ self->desk = createWindow(self->title, NULL, mask, &attrib);
+ self->shade = createWindow(self->title, NULL, mask, &attrib);
+ self->icon = createWindow(self->title, NULL, mask, &attrib);
+ self->iconify = createWindow(self->title, NULL, mask, &attrib);
+
+ self->handle = createWindow(self->window, NULL, mask, &attrib);
+ self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
+ self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
+
+ self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
+ self->handleright = createWindow(self->handle, NULL, mask, &attrib);
+
+ self->handletop = createWindow(self->window, NULL, mask, &attrib);
+ self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
+ self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
+ self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
+ self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
+ self->rgripright = createWindow(self->window, NULL, mask, &attrib);
+ self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
+ self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->focused = FALSE;
+
+ /* the other stuff is shown based on decor settings */
+ XMapWindow(obt_display, self->label);
+ XMapWindow(obt_display, self->backback);
+ XMapWindow(obt_display, self->backfront);
+
+ self->max_press = self->close_press = self->desk_press =
+ self->iconify_press = self->shade_press = FALSE;
+ self->max_hover = self->close_hover = self->desk_hover =
+ self->iconify_hover = self->shade_hover = FALSE;
+
+ /* make sure the size will be different the first time, so the extent hints
+ will be set */
+ STRUT_SET(self->oldsize, -1, -1, -1, -1);
+
+ set_theme_statics(self);
+
+ return self;
+}
+
+static void set_theme_statics(ObFrame *self)
+{
+ /* set colors/appearance/sizes for stuff that doesn't change */
+ XResizeWindow(obt_display, self->max,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->iconify,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->icon,
+ ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
+ XResizeWindow(obt_display, self->close,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->desk,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->shade,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->tltresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+ XResizeWindow(obt_display, self->trtresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+ XResizeWindow(obt_display, self->tllresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+ XResizeWindow(obt_display, self->trrresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+}
+
+static void free_theme_statics(ObFrame *self)
+{
+}
+
+void frame_free(ObFrame *self)
+{
+ free_theme_statics(self);
+
+ XDestroyWindow(obt_display, self->window);
+ if (self->colormap)
+ XFreeColormap(obt_display, self->colormap);
+
+ g_slice_free(ObFrame, self);
+}
+
+void frame_show(ObFrame *self)
+{
+ if (!self->visible) {
+ self->visible = TRUE;
+ framerender_frame(self);
+ /* Grab the server to make sure that the frame window is mapped before
+ the client gets its MapNotify, i.e. to make sure the client is
+ _visible_ when it gets MapNotify. */
+ grab_server(TRUE);
+ XMapWindow(obt_display, self->client->window);
+ XMapWindow(obt_display, self->window);
+ grab_server(FALSE);
+ }
+}
+
+void frame_hide(ObFrame *self)
+{
+ if (self->visible) {
+ self->visible = FALSE;
+ if (!frame_iconify_animating(self))
+ XUnmapWindow(obt_display, self->window);
+ /* we unmap the client itself so that we can get MapRequest
+ events, and because the ICCCM tells us to! */
+ XUnmapWindow(obt_display, self->client->window);
+ self->client->ignore_unmaps += 1;
+ }
+}
+
+void frame_adjust_theme(ObFrame *self)
+{
+ free_theme_statics(self);
+ set_theme_statics(self);
+}
+
+#ifdef SHAPE
+void frame_adjust_shape_kind(ObFrame *self, int kind)
+{
+ gint num;
+ XRectangle xrect[2];
+ gboolean shaped;
+
+ shaped = (kind == ShapeBounding && self->client->shaped);
+#ifdef ShapeInput
+ shaped |= (kind == ShapeInput && self->client->shaped_input);
+#endif
+
+ if (!shaped) {
+ /* clear the shape on the frame window */
+ XShapeCombineMask(obt_display, self->window, kind,
+ self->size.left,
+ self->size.top,
+ None, ShapeSet);
+ } else {
+ /* make the frame's shape match the clients */
+ XShapeCombineShape(obt_display, self->window, kind,
+ self->size.left,
+ self->size.top,
+ self->client->window,
+ kind, ShapeSet);
+
+ num = 0;
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ xrect[0].x = 0;
+ xrect[0].y = 0;
+ xrect[0].width = self->area.width;
+ xrect[0].height = self->size.top;
+ ++num;
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ xrect[1].x = 0;
+ xrect[1].y = FRAME_HANDLE_Y(self);
+ xrect[1].width = self->area.width;
+ xrect[1].height = ob_rr_theme->handle_height +
+ self->bwidth * 2;
+ ++num;
+ }
+
+ XShapeCombineRectangles(obt_display, self->window,
+ ShapeBounding, 0, 0, xrect, num,
+ ShapeUnion, Unsorted);
+ }
+}
+#endif
+
+void frame_adjust_shape(ObFrame *self)
+{
+#ifdef SHAPE
+ frame_adjust_shape_kind(self, ShapeBounding);
+#ifdef ShapeInput
+ frame_adjust_shape_kind(self, ShapeInput);
+#endif
+#endif
+}
+
+void frame_adjust_area(ObFrame *self, gboolean moved,
+ gboolean resized, gboolean fake)
+{
+ if (resized) {
+ /* do this before changing the frame's status like max_horz max_vert */
+ frame_adjust_cursors(self);
+
+ self->functions = self->client->functions;
+ self->decorations = self->client->decorations;
+ self->max_horz = self->client->max_horz;
+ self->max_vert = self->client->max_vert;
+ self->shaded = self->client->shaded;
+
+ if (self->decorations & OB_FRAME_DECOR_BORDER)
+ self->bwidth = self->client->undecorated ?
+ ob_rr_theme->ubwidth : ob_rr_theme->fbwidth;
+ else
+ self->bwidth = 0;
+
+ if (self->decorations & OB_FRAME_DECOR_BORDER &&
+ !self->client->undecorated)
+ {
+ self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
+ self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
+ } else
+ self->cbwidth_l = self->cbwidth_t =
+ self->cbwidth_r = self->cbwidth_b = 0;
+
+ if (self->max_horz) {
+ self->cbwidth_l = self->cbwidth_r = 0;
+ self->width = self->client->area.width;
+ if (self->max_vert)
+ self->cbwidth_b = 0;
+ } else
+ self->width = self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r;
+
+ /* some elements are sized based of the width, so don't let them have
+ negative values */
+ self->width = MAX(self->width,
+ (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
+
+ STRUT_SET(self->size,
+ self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
+ self->cbwidth_t +
+ (!self->max_horz || !self->max_vert ? self->bwidth : 0),
+ self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
+ self->cbwidth_b +
+ (!self->max_horz || !self->max_vert ? self->bwidth : 0));
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
+ self->size.top += ob_rr_theme->title_height + self->bwidth;
+ else if (self->max_horz && self->max_vert) {
+ /* A maximized and undecorated window needs a border on the
+ top of the window to let the user still undecorate/unmaximize the
+ window via the client menu. */
+ self->size.top += self->bwidth;
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
+ }
+
+ /* position/size and map/unmap all the windows */
+
+ if (!fake) {
+ gint innercornerheight =
+ ob_rr_theme->grip_width - self->size.bottom;
+
+ if (self->cbwidth_l) {
+ XMoveResizeWindow(obt_display, self->innerleft,
+ self->size.left - self->cbwidth_l,
+ self->size.top,
+ self->cbwidth_l, self->client->area.height);
+
+ XMapWindow(obt_display, self->innerleft);
+ } else
+ XUnmapWindow(obt_display, self->innerleft);
+
+ if (self->cbwidth_l && innercornerheight > 0) {
+ XMoveResizeWindow(obt_display, self->innerbll,
+ 0,
+ self->client->area.height -
+ (ob_rr_theme->grip_width -
+ self->size.bottom),
+ self->cbwidth_l,
+ ob_rr_theme->grip_width - self->size.bottom);
+
+ XMapWindow(obt_display, self->innerbll);
+ } else
+ XUnmapWindow(obt_display, self->innerbll);
+
+ if (self->cbwidth_r) {
+ XMoveResizeWindow(obt_display, self->innerright,
+ self->size.left + self->client->area.width,
+ self->size.top,
+ self->cbwidth_r, self->client->area.height);
+
+ XMapWindow(obt_display, self->innerright);
+ } else
+ XUnmapWindow(obt_display, self->innerright);
+
+ if (self->cbwidth_r && innercornerheight > 0) {
+ XMoveResizeWindow(obt_display, self->innerbrr,
+ 0,
+ self->client->area.height -
+ (ob_rr_theme->grip_width -
+ self->size.bottom),
+ self->cbwidth_r,
+ ob_rr_theme->grip_width - self->size.bottom);
+
+ XMapWindow(obt_display, self->innerbrr);
+ } else
+ XUnmapWindow(obt_display, self->innerbrr);
+
+ if (self->cbwidth_t) {
+ XMoveResizeWindow(obt_display, self->innertop,
+ self->size.left - self->cbwidth_l,
+ self->size.top - self->cbwidth_t,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r,
+ self->cbwidth_t);
+
+ XMapWindow(obt_display, self->innertop);
+ } else
+ XUnmapWindow(obt_display, self->innertop);
+
+ if (self->cbwidth_b) {
+ XMoveResizeWindow(obt_display, self->innerbottom,
+ self->size.left - self->cbwidth_l,
+ self->size.top + self->client->area.height,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r,
+ self->cbwidth_b);
+
+ XMoveResizeWindow(obt_display, self->innerblb,
+ 0, 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->cbwidth_b);
+ XMoveResizeWindow(obt_display, self->innerbrb,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r -
+ (ob_rr_theme->grip_width + self->bwidth),
+ 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->cbwidth_b);
+
+ XMapWindow(obt_display, self->innerbottom);
+ XMapWindow(obt_display, self->innerblb);
+ XMapWindow(obt_display, self->innerbrb);
+ } else {
+ XUnmapWindow(obt_display, self->innerbottom);
+ XUnmapWindow(obt_display, self->innerblb);
+ XUnmapWindow(obt_display, self->innerbrb);
+ }
+
+ if (self->bwidth) {
+ gint titlesides;
+
+ /* height of titleleft and titleright */
+ titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
+
+ XMoveResizeWindow(obt_display, self->titletop,
+ ob_rr_theme->grip_width + self->bwidth, 0,
+ /* width + bwidth*2 - bwidth*2 - grips*2 */
+ self->width - ob_rr_theme->grip_width * 2,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->titletopleft,
+ 0, 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->titletopright,
+ self->client->area.width +
+ self->size.left + self->size.right -
+ ob_rr_theme->grip_width - self->bwidth,
+ 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+
+ if (titlesides > 0) {
+ XMoveResizeWindow(obt_display, self->titleleft,
+ 0, self->bwidth,
+ self->bwidth,
+ titlesides);
+ XMoveResizeWindow(obt_display, self->titleright,
+ self->client->area.width +
+ self->size.left + self->size.right -
+ self->bwidth,
+ self->bwidth,
+ self->bwidth,
+ titlesides);
+
+ XMapWindow(obt_display, self->titleleft);
+ XMapWindow(obt_display, self->titleright);
+ } else {
+ XUnmapWindow(obt_display, self->titleleft);
+ XUnmapWindow(obt_display, self->titleright);
+ }
+
+ XMapWindow(obt_display, self->titletop);
+ XMapWindow(obt_display, self->titletopleft);
+ XMapWindow(obt_display, self->titletopright);
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ XMoveResizeWindow(obt_display, self->titlebottom,
+ (self->max_horz ? 0 : self->bwidth),
+ ob_rr_theme->title_height + self->bwidth,
+ self->width,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->titlebottom);
+ } else
+ XUnmapWindow(obt_display, self->titlebottom);
+ } else {
+ XUnmapWindow(obt_display, self->titlebottom);
+
+ XUnmapWindow(obt_display, self->titletop);
+ XUnmapWindow(obt_display, self->titletopleft);
+ XUnmapWindow(obt_display, self->titletopright);
+ XUnmapWindow(obt_display, self->titleleft);
+ XUnmapWindow(obt_display, self->titleright);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ XMoveResizeWindow(obt_display, self->title,
+ (self->max_horz ? 0 : self->bwidth),
+ self->bwidth,
+ self->width, ob_rr_theme->title_height);
+
+ XMapWindow(obt_display, self->title);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->topresize,
+ ob_rr_theme->grip_width,
+ 0,
+ self->width - ob_rr_theme->grip_width *2,
+ ob_rr_theme->paddingy + 1);
+
+ XMoveWindow(obt_display, self->tltresize, 0, 0);
+ XMoveWindow(obt_display, self->tllresize, 0, 0);
+ XMoveWindow(obt_display, self->trtresize,
+ self->width - ob_rr_theme->grip_width, 0);
+ XMoveWindow(obt_display, self->trrresize,
+ self->width - ob_rr_theme->paddingx - 1, 0);
+
+ XMapWindow(obt_display, self->topresize);
+ XMapWindow(obt_display, self->tltresize);
+ XMapWindow(obt_display, self->tllresize);
+ XMapWindow(obt_display, self->trtresize);
+ XMapWindow(obt_display, self->trrresize);
+ } else {
+ XUnmapWindow(obt_display, self->topresize);
+ XUnmapWindow(obt_display, self->tltresize);
+ XUnmapWindow(obt_display, self->tllresize);
+ XUnmapWindow(obt_display, self->trtresize);
+ XUnmapWindow(obt_display, self->trrresize);
+ }
+ } else
+ XUnmapWindow(obt_display, self->title);
+ }
+
+ if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ /* layout the title bar elements */
+ layout_title(self);
+
+ if (!fake) {
+ gint sidebwidth = self->max_horz ? 0 : self->bwidth;
+
+ if (self->bwidth && self->size.bottom) {
+ XMoveResizeWindow(obt_display, self->handlebottom,
+ ob_rr_theme->grip_width +
+ self->bwidth + sidebwidth,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ self->width - (ob_rr_theme->grip_width +
+ sidebwidth) * 2,
+ self->bwidth);
+
+
+ if (sidebwidth) {
+ XMoveResizeWindow(obt_display, self->lgripleft,
+ 0,
+ self->size.top +
+ self->client->area.height +
+ self->size.bottom -
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b),
+ self->bwidth,
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b));
+ XMoveResizeWindow(obt_display, self->rgripright,
+ self->size.left +
+ self->client->area.width +
+ self->size.right - self->bwidth,
+ self->size.top +
+ self->client->area.height +
+ self->size.bottom -
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b),
+ self->bwidth,
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b));
+
+ XMapWindow(obt_display, self->lgripleft);
+ XMapWindow(obt_display, self->rgripright);
+ } else {
+ XUnmapWindow(obt_display, self->lgripleft);
+ XUnmapWindow(obt_display, self->rgripright);
+ }
+
+ XMoveResizeWindow(obt_display, self->lgripbottom,
+ sidebwidth,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->rgripbottom,
+ self->size.left + self->client->area.width +
+ self->size.right - self->bwidth - sidebwidth-
+ ob_rr_theme->grip_width,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->handlebottom);
+ XMapWindow(obt_display, self->lgripbottom);
+ XMapWindow(obt_display, self->rgripbottom);
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ XMoveResizeWindow(obt_display, self->handletop,
+ ob_rr_theme->grip_width +
+ self->bwidth + sidebwidth,
+ FRAME_HANDLE_Y(self),
+ self->width - (ob_rr_theme->grip_width +
+ sidebwidth) * 2,
+ self->bwidth);
+ XMapWindow(obt_display, self->handletop);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->handleleft,
+ ob_rr_theme->grip_width,
+ 0,
+ self->bwidth,
+ ob_rr_theme->handle_height);
+ XMoveResizeWindow(obt_display, self->handleright,
+ self->width -
+ ob_rr_theme->grip_width -
+ self->bwidth,
+ 0,
+ self->bwidth,
+ ob_rr_theme->handle_height);
+
+ XMoveResizeWindow(obt_display, self->lgriptop,
+ sidebwidth,
+ FRAME_HANDLE_Y(self),
+ ob_rr_theme->grip_width +
+ self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->rgriptop,
+ self->size.left +
+ self->client->area.width +
+ self->size.right - self->bwidth -
+ sidebwidth - ob_rr_theme->grip_width,
+ FRAME_HANDLE_Y(self),
+ ob_rr_theme->grip_width +
+ self->bwidth,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->handleleft);
+ XMapWindow(obt_display, self->handleright);
+ XMapWindow(obt_display, self->lgriptop);
+ XMapWindow(obt_display, self->rgriptop);
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+
+ XUnmapWindow(obt_display, self->handletop);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+
+ XUnmapWindow(obt_display, self->handletop);
+
+ XUnmapWindow(obt_display, self->handlebottom);
+ XUnmapWindow(obt_display, self->lgripleft);
+ XUnmapWindow(obt_display, self->rgripright);
+ XUnmapWindow(obt_display, self->lgripbottom);
+ XUnmapWindow(obt_display, self->rgripbottom);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ XMoveResizeWindow(obt_display, self->handle,
+ sidebwidth,
+ FRAME_HANDLE_Y(self) + self->bwidth,
+ self->width, ob_rr_theme->handle_height);
+ XMapWindow(obt_display, self->handle);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->lgrip,
+ 0, 0,
+ ob_rr_theme->grip_width,
+ ob_rr_theme->handle_height);
+ XMoveResizeWindow(obt_display, self->rgrip,
+ self->width - ob_rr_theme->grip_width,
+ 0,
+ ob_rr_theme->grip_width,
+ ob_rr_theme->handle_height);
+
+ XMapWindow(obt_display, self->lgrip);
+ XMapWindow(obt_display, self->rgrip);
+ } else {
+ XUnmapWindow(obt_display, self->lgrip);
+ XUnmapWindow(obt_display, self->rgrip);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->lgrip);
+ XUnmapWindow(obt_display, self->rgrip);
+
+ XUnmapWindow(obt_display, self->handle);
+ }
+
+ if (self->bwidth && !self->max_horz &&
+ (self->client->area.height + self->size.top +
+ self->size.bottom) > ob_rr_theme->grip_width * 2)
+ {
+ XMoveResizeWindow(obt_display, self->left,
+ 0,
+ self->bwidth + ob_rr_theme->grip_width,
+ self->bwidth,
+ self->client->area.height +
+ self->size.top + self->size.bottom -
+ ob_rr_theme->grip_width * 2);
+
+ XMapWindow(obt_display, self->left);
+ } else
+ XUnmapWindow(obt_display, self->left);
+
+ if (self->bwidth && !self->max_horz &&
+ (self->client->area.height + self->size.top +
+ self->size.bottom) > ob_rr_theme->grip_width * 2)
+ {
+ XMoveResizeWindow(obt_display, self->right,
+ self->client->area.width + self->cbwidth_l +
+ self->cbwidth_r + self->bwidth,
+ self->bwidth + ob_rr_theme->grip_width,
+ self->bwidth,
+ self->client->area.height +
+ self->size.top + self->size.bottom -
+ ob_rr_theme->grip_width * 2);
+
+ XMapWindow(obt_display, self->right);
+ } else
+ XUnmapWindow(obt_display, self->right);
+
+ XMoveResizeWindow(obt_display, self->backback,
+ self->size.left, self->size.top,
+ self->client->area.width,
+ self->client->area.height);
+ }
+ }
+
+ /* shading can change without being moved or resized */
+ RECT_SET_SIZE(self->area,
+ self->client->area.width +
+ self->size.left + self->size.right,
+ (self->client->shaded ?
+ ob_rr_theme->title_height + self->bwidth * 2:
+ self->client->area.height +
+ self->size.top + self->size.bottom));
+
+ if ((moved || resized) && !fake) {
+ /* find the new coordinates, done after setting the frame.size, for
+ frame_client_gravity. */
+ self->area.x = self->client->area.x;
+ self->area.y = self->client->area.y;
+ frame_client_gravity(self, &self->area.x, &self->area.y);
+ }
+
+ if (!fake) {
+ if (!frame_iconify_animating(self))
+ /* move and resize the top level frame.
+ shading can change without being moved or resized.
+
+ but don't do this during an iconify animation. it will be
+ reflected afterwards.
+ */
+ XMoveResizeWindow(obt_display, self->window,
+ self->area.x,
+ self->area.y,
+ self->area.width,
+ self->area.height);
+
+ /* when the client has StaticGravity, it likes to move around.
+ also this correctly positions the client when it maps.
+ this also needs to be run when the frame's decorations sizes change!
+ */
+ XMoveWindow(obt_display, self->client->window,
+ self->size.left, self->size.top);
+
+ if (resized) {
+ self->need_render = TRUE;
+ framerender_frame(self);
+ frame_adjust_shape(self);
+ }
+
+ if (!STRUT_EQUAL(self->size, self->oldsize)) {
+ gulong vals[4];
+ vals[0] = self->size.left;
+ vals[1] = self->size.right;
+ vals[2] = self->size.top;
+ vals[3] = self->size.bottom;
+ OBT_PROP_SETA32(self->client->window, NET_FRAME_EXTENTS,
+ CARDINAL, vals, 4);
+ OBT_PROP_SETA32(self->client->window, KDE_NET_WM_FRAME_STRUT,
+ CARDINAL, vals, 4);
+ self->oldsize = self->size;
+ }
+
+ /* if this occurs while we are focus cycling, the indicator needs to
+ match the changes */
+ if (focus_cycle_target == self->client)
+ focus_cycle_update_indicator(self->client);
+ }
+ if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) &&
+ self->label_width)
+ {
+ XResizeWindow(obt_display, self->label, self->label_width,
+ ob_rr_theme->label_height);
+ }
+}
+
+static void frame_adjust_cursors(ObFrame *self)
+{
+ if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
+ (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
+ self->max_horz != self->client->max_horz ||
+ self->max_vert != self->client->max_vert ||
+ self->shaded != self->client->shaded)
+ {
+ gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
+ !(self->client->max_horz && self->client->max_vert);
+ gboolean topbot = !self->client->max_vert;
+ gboolean sh = self->client->shaded;
+ XSetWindowAttributes a;
+
+ /* these ones turn off when max vert, and some when shaded */
+ a.cursor = ob_cursor(r && topbot && !sh ?
+ OB_CURSOR_NORTH : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a);
+ a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a);
+
+ /* these ones change when shaded */
+ a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
+ OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a);
+ a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
+ OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a);
+
+ /* these ones are pretty static */
+ a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->left, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->right, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a);
+ }
+}
+
+void frame_adjust_client_area(ObFrame *self)
+{
+ /* adjust the window which is there to prevent flashing on unmap */
+ XMoveResizeWindow(obt_display, self->backfront, 0, 0,
+ self->client->area.width,
+ self->client->area.height);
+}
+
+void frame_adjust_state(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_adjust_focus(ObFrame *self, gboolean hilite)
+{
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Frame for 0x%x has focus: %d",
+ self->client->window, hilite);
+ self->focused = hilite;
+ self->need_render = TRUE;
+ framerender_frame(self);
+ XFlush(obt_display);
+}
+
+void frame_adjust_title(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_adjust_icon(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_grab_client(ObFrame *self)
+{
+ /* DO NOT map the client window here. we used to do that, but it is bogus.
+ we need to set up the client's dimensions and everything before we
+ send a mapnotify or we create race conditions.
+ */
+
+ /* reparent the client to the frame */
+ XReparentWindow(obt_display, self->client->window, self->window, 0, 0);
+
+ /*
+ When reparenting the client window, it is usually not mapped yet, since
+ this occurs from a MapRequest. However, in the case where Openbox is
+ starting up, the window is already mapped, so we'll see an unmap event
+ for it.
+ */
+ if (ob_state() == OB_STATE_STARTING)
+ ++self->client->ignore_unmaps;
+
+ /* select the event mask on the client's parent (to receive config/map
+ req's) the ButtonPress is to catch clicks on the client border */
+ XSelectInput(obt_display, self->window, FRAME_EVENTMASK);
+
+ /* set all the windows for the frame in the window_map */
+ window_add(&self->window, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->backback, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->backfront, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innertop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->title, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->label, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->max, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->close, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->desk, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->shade, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->icon, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->iconify, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handle, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->topresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->left, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->right, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titleright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handletop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handleright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
+}
+
+static gboolean find_reparent(XEvent *e, gpointer data)
+{
+ const ObFrame *self = data;
+
+ /* Find ReparentNotify events for the window that aren't being reparented into the
+ frame, thus the client reparenting itself off the frame. */
+ return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
+ e->xreparent.parent != self->window;
+}
+
+void frame_release_client(ObFrame *self)
+{
+ /* if there was any animation going on, kill it */
+ if (self->iconify_animation_timer)
+ g_source_remove(self->iconify_animation_timer);
+
+ /* check if the app has already reparented its window away */
+ if (!xqueue_exists_local(find_reparent, self)) {
+ /* according to the ICCCM - if the client doesn't reparent itself,
+ then we will reparent the window to root for them */
+ XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
+ self->client->area.x, self->client->area.y);
+ }
+
+ /* remove all the windows for the frame from the window_map */
+ window_remove(self->window);
+ window_remove(self->backback);
+ window_remove(self->backfront);
+ window_remove(self->innerleft);
+ window_remove(self->innertop);
+ window_remove(self->innerright);
+ window_remove(self->innerbottom);
+ window_remove(self->innerblb);
+ window_remove(self->innerbll);
+ window_remove(self->innerbrb);
+ window_remove(self->innerbrr);
+ window_remove(self->title);
+ window_remove(self->label);
+ window_remove(self->max);
+ window_remove(self->close);
+ window_remove(self->desk);
+ window_remove(self->shade);
+ window_remove(self->icon);
+ window_remove(self->iconify);
+ window_remove(self->handle);
+ window_remove(self->lgrip);
+ window_remove(self->rgrip);
+ window_remove(self->topresize);
+ window_remove(self->tltresize);
+ window_remove(self->tllresize);
+ window_remove(self->trtresize);
+ window_remove(self->trrresize);
+ window_remove(self->left);
+ window_remove(self->right);
+ window_remove(self->titleleft);
+ window_remove(self->titletop);
+ window_remove(self->titletopleft);
+ window_remove(self->titletopright);
+ window_remove(self->titleright);
+ window_remove(self->titlebottom);
+ window_remove(self->handleleft);
+ window_remove(self->handletop);
+ window_remove(self->handleright);
+ window_remove(self->handlebottom);
+ window_remove(self->lgripleft);
+ window_remove(self->lgriptop);
+ window_remove(self->lgripbottom);
+ window_remove(self->rgripright);
+ window_remove(self->rgriptop);
+ window_remove(self->rgripbottom);
+
+ if (self->flash_timer) g_source_remove(self->flash_timer);
+}
+
+/* is there anything present between us and the label? */
+static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
+ for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
+ if (*lc == ' ') continue; /* it was invalid */
+ if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
+ return TRUE;
+ if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
+ return TRUE;
+ if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
+ return TRUE;
+ if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
+ return TRUE;
+ if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
+ return TRUE;
+ if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
+ return TRUE;
+ if (*lc == 'L') return FALSE;
+ }
+ return FALSE;
+}
+
+static void place_button(ObFrame *self, const char *lc, gint bwidth,
+ gint left, gint i,
+ gint *x, gint *button_on, gint *button_x)
+{
+ if (!(*button_on = is_button_present(self, lc, i)))
+ return;
+
+ self->label_width -= bwidth;
+ if (i > 0)
+ *button_x = *x;
+ *x += i * bwidth;
+ if (i < 0) {
+ if (self->label_x <= left || *x > self->label_x) {
+ *button_x = *x;
+ } else {
+ /* the button would have been drawn on top of another button */
+ *button_on = FALSE;
+ self->label_width += bwidth;
+ }
+ }
+}
+
+static void layout_title(ObFrame *self)
+{
+ gchar *lc;
+ gint i;
+
+ const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
+ /* position of the leftmost button */
+ const gint left = ob_rr_theme->paddingx + 1;
+ /* position of the rightmost button */
+ const gint right = self->width;
+
+ /* turn them all off */
+ self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
+ self->max_on = self->close_on = self->label_on = FALSE;
+ self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
+ self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
+
+ /* figure out what's being shown, find each element's position, and the
+ width of the label
+
+ do the ones before the label, then after the label,
+ i will be +1 the first time through when working to the left,
+ and -1 the second time through when working to the right */
+ for (i = 1; i >= -1; i-=2) {
+ gint x;
+ ObFrameContext *firstcon;
+
+ if (i > 0) {
+ x = left;
+ lc = config_title_layout;
+ firstcon = &self->leftmost;
+ } else {
+ x = right;
+ lc = config_title_layout + strlen(config_title_layout)-1;
+ firstcon = &self->rightmost;
+ }
+
+ /* stop at the end of the string (or the label, which calls break) */
+ for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
+ if (*lc == 'L') {
+ if (i > 0) {
+ self->label_on = TRUE;
+ self->label_x = x;
+ }
+ break; /* break the for loop, do other side of label */
+ } else if (*lc == 'N') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
+ /* icon is bigger than buttons */
+ place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
+ } else if (*lc == 'D') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
+ place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
+ } else if (*lc == 'S') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
+ place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
+ } else if (*lc == 'I') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
+ place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
+ } else if (*lc == 'M') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
+ place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
+ } else if (*lc == 'C') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
+ place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
+ } else
+ continue; /* don't set firstcon */
+ firstcon = NULL;
+ }
+ }
+
+ /* position and map the elements */
+ if (self->icon_on) {
+ XMapWindow(obt_display, self->icon);
+ XMoveWindow(obt_display, self->icon, self->icon_x,
+ ob_rr_theme->paddingy);
+ } else
+ XUnmapWindow(obt_display, self->icon);
+
+ if (self->desk_on) {
+ XMapWindow(obt_display, self->desk);
+ XMoveWindow(obt_display, self->desk, self->desk_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->desk);
+
+ if (self->shade_on) {
+ XMapWindow(obt_display, self->shade);
+ XMoveWindow(obt_display, self->shade, self->shade_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->shade);
+
+ if (self->iconify_on) {
+ XMapWindow(obt_display, self->iconify);
+ XMoveWindow(obt_display, self->iconify, self->iconify_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->iconify);
+
+ if (self->max_on) {
+ XMapWindow(obt_display, self->max);
+ XMoveWindow(obt_display, self->max, self->max_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->max);
+
+ if (self->close_on) {
+ XMapWindow(obt_display, self->close);
+ XMoveWindow(obt_display, self->close, self->close_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->close);
+
+ if (self->label_on && self->label_width > 0) {
+ XMapWindow(obt_display, self->label);
+ XMoveWindow(obt_display, self->label, self->label_x,
+ ob_rr_theme->paddingy);
+ } else
+ XUnmapWindow(obt_display, self->label);
+}
+
+gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx)
+{
+ gchar *p, *n;
+
+ if (!*names) /* empty string */
+ return FALSE;
+
+ /* find the first space */
+ for (p = names; *p; p = g_utf8_next_char(p)) {
+ const gunichar c = g_utf8_get_char(p);
+ if (g_unichar_isspace(c)) break;
+ }
+
+ if (p == names) {
+ /* leading spaces in the string */
+ n = g_utf8_next_char(names);
+ if (!frame_next_context_from_string(n, cx))
+ return FALSE;
+ } else {
+ n = p;
+ if (*p) {
+ /* delete the space with null zero(s) */
+ while (n < g_utf8_next_char(p))
+ *(n++) = '\0';
+ }
+
+ *cx = frame_context_from_string(names);
+
+ /* find the next non-space */
+ for (; *n; n = g_utf8_next_char(n)) {
+ const gunichar c = g_utf8_get_char(n);
+ if (!g_unichar_isspace(c)) break;
+ }
+ }
+
+ /* delete everything we just read (copy everything at n to the start of
+ the string */
+ for (p = names; *n; ++p, ++n)
+ *p = *n;
+ *p = *n;
+
+ return TRUE;
+}
+
+ObFrameContext frame_context_from_string(const gchar *name)
+{
+ if (!g_ascii_strcasecmp("Desktop", name))
+ return OB_FRAME_CONTEXT_DESKTOP;
+ else if (!g_ascii_strcasecmp("Root", name))
+ return OB_FRAME_CONTEXT_ROOT;
+ else if (!g_ascii_strcasecmp("Client", name))
+ return OB_FRAME_CONTEXT_CLIENT;
+ else if (!g_ascii_strcasecmp("Titlebar", name))
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ else if (!g_ascii_strcasecmp("Frame", name))
+ return OB_FRAME_CONTEXT_FRAME;
+ else if (!g_ascii_strcasecmp("TLCorner", name))
+ return OB_FRAME_CONTEXT_TLCORNER;
+ else if (!g_ascii_strcasecmp("TRCorner", name))
+ return OB_FRAME_CONTEXT_TRCORNER;
+ else if (!g_ascii_strcasecmp("BLCorner", name))
+ return OB_FRAME_CONTEXT_BLCORNER;
+ else if (!g_ascii_strcasecmp("BRCorner", name))
+ return OB_FRAME_CONTEXT_BRCORNER;
+ else if (!g_ascii_strcasecmp("Top", name))
+ return OB_FRAME_CONTEXT_TOP;
+ else if (!g_ascii_strcasecmp("Bottom", name))
+ return OB_FRAME_CONTEXT_BOTTOM;
+ else if (!g_ascii_strcasecmp("Left", name))
+ return OB_FRAME_CONTEXT_LEFT;
+ else if (!g_ascii_strcasecmp("Right", name))
+ return OB_FRAME_CONTEXT_RIGHT;
+ else if (!g_ascii_strcasecmp("Maximize", name))
+ return OB_FRAME_CONTEXT_MAXIMIZE;
+ else if (!g_ascii_strcasecmp("AllDesktops", name))
+ return OB_FRAME_CONTEXT_ALLDESKTOPS;
+ else if (!g_ascii_strcasecmp("Shade", name))
+ return OB_FRAME_CONTEXT_SHADE;
+ else if (!g_ascii_strcasecmp("Iconify", name))
+ return OB_FRAME_CONTEXT_ICONIFY;
+ else if (!g_ascii_strcasecmp("Icon", name))
+ return OB_FRAME_CONTEXT_ICON;
+ else if (!g_ascii_strcasecmp("Close", name))
+ return OB_FRAME_CONTEXT_CLOSE;
+ else if (!g_ascii_strcasecmp("MoveResize", name))
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+ else if (!g_ascii_strcasecmp("Dock", name))
+ return OB_FRAME_CONTEXT_DOCK;
+
+ return OB_FRAME_CONTEXT_NONE;
+}
+
+ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
+{
+ ObFrame *self;
+ ObWindow *obwin;
+
+ if (moveresize_in_progress)
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+
+ if (win == obt_root(ob_screen))
+ return OB_FRAME_CONTEXT_ROOT;
+ if ((obwin = window_find(win))) {
+ if (WINDOW_IS_DOCK(obwin)) {
+ return OB_FRAME_CONTEXT_DOCK;
+ }
+ }
+ if (client == NULL) return OB_FRAME_CONTEXT_NONE;
+ if (win == client->window) {
+ /* conceptually, this is the desktop, as far as users are
+ concerned */
+ if (client->type == OB_CLIENT_TYPE_DESKTOP)
+ return OB_FRAME_CONTEXT_DESKTOP;
+ return OB_FRAME_CONTEXT_CLIENT;
+ }
+
+ self = client->frame;
+
+ /* when the user clicks in the corners of the titlebar and the client
+ is fully maximized, then treat it like they clicked in the
+ button that is there */
+ if (self->max_horz && self->max_vert &&
+ (win == self->title || win == self->titletop ||
+ win == self->titleleft || win == self->titletopleft ||
+ win == self->titleright || win == self->titletopright))
+ {
+ /* get the mouse coords in reference to the whole frame */
+ gint fx = x;
+ gint fy = y;
+
+ /* these windows are down a border width from the top of the frame */
+ if (win == self->title ||
+ win == self->titleleft || win == self->titleright)
+ fy += self->bwidth;
+
+ /* title is a border width in from the edge */
+ if (win == self->title)
+ fx += self->bwidth;
+ /* titletop is a bit to the right */
+ else if (win == self->titletop)
+ fx += ob_rr_theme->grip_width + self->bwidth;
+ /* titletopright is way to the right edge */
+ else if (win == self->titletopright)
+ fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
+ /* titleright is even more way to the right edge */
+ else if (win == self->titleright)
+ fx += self->area.width - self->bwidth;
+
+ /* figure out if we're over the area that should be considered a
+ button */
+ if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
+ ob_rr_theme->button_size)
+ {
+ if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
+ ob_rr_theme->button_size))
+ {
+ if (self->leftmost != OB_FRAME_CONTEXT_NONE)
+ return self->leftmost;
+ }
+ else if (fx >= (self->area.width -
+ (self->bwidth + ob_rr_theme->paddingx + 1 +
+ ob_rr_theme->button_size)))
+ {
+ if (self->rightmost != OB_FRAME_CONTEXT_NONE)
+ return self->rightmost;
+ }
+ }
+
+ /* there is no resizing maximized windows so make them the titlebar
+ context */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ }
+ else if (self->max_vert &&
+ (win == self->titletop || win == self->topresize))
+ /* can't resize vertically when max vert */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ else if (self->shaded &&
+ (win == self->titletop || win == self->topresize))
+ /* can't resize vertically when shaded */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+
+ if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
+ if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
+ if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
+ if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->left) return OB_FRAME_CONTEXT_LEFT;
+ if (win == self->right) return OB_FRAME_CONTEXT_RIGHT;
+ if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT;
+ if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT;
+ if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
+ if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
+ if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
+ if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
+ if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
+ if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
+
+ return OB_FRAME_CONTEXT_NONE;
+}
+
+void frame_client_gravity(ObFrame *self, gint *x, gint *y)
+{
+ /* horizontal */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case WestGravity:
+ break;
+
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ /* the middle of the client will be the middle of the frame */
+ *x -= (self->size.right - self->size.left) / 2;
+ break;
+
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case EastGravity:
+ /* the right side of the client will be the right side of the frame */
+ *x -= self->size.right + self->size.left -
+ self->client->border_width * 2;
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ /* the client's position won't move */
+ *x -= self->size.left - self->client->border_width;
+ break;
+ }
+
+ /* vertical */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthEastGravity:
+ case NorthGravity:
+ break;
+
+ case CenterGravity:
+ case EastGravity:
+ case WestGravity:
+ /* the middle of the client will be the middle of the frame */
+ *y -= (self->size.bottom - self->size.top) / 2;
+ break;
+
+ case SouthWestGravity:
+ case SouthEastGravity:
+ case SouthGravity:
+ /* the bottom of the client will be the bottom of the frame */
+ *y -= self->size.bottom + self->size.top -
+ self->client->border_width * 2;
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ /* the client's position won't move */
+ *y -= self->size.top - self->client->border_width;
+ break;
+ }
+}
+
+void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
+{
+ /* horizontal */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ break;
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ /* the middle of the client will be the middle of the frame */
+ *x += (self->size.right - self->size.left) / 2;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ /* the right side of the client will be the right side of the frame */
+ *x += self->size.right + self->size.left -
+ self->client->border_width * 2;
+ break;
+ case StaticGravity:
+ case ForgetGravity:
+ /* the client's position won't move */
+ *x += self->size.left - self->client->border_width;
+ break;
+ }
+
+ /* vertical */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ break;
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ /* the middle of the client will be the middle of the frame */
+ *y += (self->size.bottom - self->size.top) / 2;
+ break;
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ /* the bottom of the client will be the bottom of the frame */
+ *y += self->size.bottom + self->size.top -
+ self->client->border_width * 2;
+ break;
+ case StaticGravity:
+ case ForgetGravity:
+ /* the client's position won't move */
+ *y += self->size.top - self->client->border_width;
+ break;
+ }
+}
+
+void frame_rect_to_frame(ObFrame *self, Rect *r)
+{
+ r->width += self->size.left + self->size.right;
+ r->height += self->size.top + self->size.bottom;
+ frame_client_gravity(self, &r->x, &r->y);
+}
+
+void frame_rect_to_client(ObFrame *self, Rect *r)
+{
+ r->width -= self->size.left + self->size.right;
+ r->height -= self->size.top + self->size.bottom;
+ frame_frame_gravity(self, &r->x, &r->y);
+}
+
+static void flash_done(gpointer data)
+{
+ ObFrame *self = data;
+
+ self->flash_timer = 0;
+}
+
+static gboolean flash_timeout(gpointer data)
+{
+ ObFrame *self = data;
+ GTimeVal now;
+
+ g_get_current_time(&now);
+ if (now.tv_sec > self->flash_end.tv_sec ||
+ (now.tv_sec == self->flash_end.tv_sec &&
+ now.tv_usec >= self->flash_end.tv_usec))
+ self->flashing = FALSE;
+
+ if (!self->flashing) {
+ if (self->focused != self->flash_on)
+ frame_adjust_focus(self, self->focused);
+
+ return FALSE; /* we are done */
+ }
+
+ self->flash_on = !self->flash_on;
+ if (!self->focused) {
+ frame_adjust_focus(self, self->flash_on);
+ self->focused = FALSE;
+ }
+
+ return TRUE; /* go again */
+}
+
+void frame_flash_start(ObFrame *self)
+{
+ self->flash_on = self->focused;
+
+ if (!self->flashing)
+ self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 600, flash_timeout, self,
+ flash_done);
+ g_get_current_time(&self->flash_end);
+ g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
+
+ self->flashing = TRUE;
+}
+
+void frame_flash_stop(ObFrame *self)
+{
+ self->flashing = FALSE;
+}
+
+static gulong frame_animate_iconify_time_left(ObFrame *self,
+ const GTimeVal *now)
+{
+ glong sec, usec;
+ sec = self->iconify_animation_end.tv_sec - now->tv_sec;
+ usec = self->iconify_animation_end.tv_usec - now->tv_usec;
+ if (usec < 0) {
+ usec += G_USEC_PER_SEC;
+ sec--;
+ }
+ /* no negative values */
+ return MAX(sec * G_USEC_PER_SEC + usec, 0);
+}
+
+static gboolean frame_animate_iconify(gpointer p)
+{
+ ObFrame *self = p;
+ gint x, y, w, h;
+ gint iconx, icony, iconw;
+ GTimeVal now;
+ gulong time;
+ gboolean iconifying;
+
+ if (self->client->icon_geometry.width == 0) {
+ /* there is no icon geometry set so just go straight down */
+ const Rect *a;
+
+ a = screen_physical_area_monitor(screen_find_monitor(&self->area));
+ iconx = self->area.x + self->area.width / 2 + 32;
+ icony = a->y + a->width;
+ iconw = 64;
+ } else {
+ iconx = self->client->icon_geometry.x;
+ icony = self->client->icon_geometry.y;
+ iconw = self->client->icon_geometry.width;
+ }
+
+ iconifying = self->iconify_animation_going > 0;
+
+ /* how far do we have left to go ? */
+ g_get_current_time(&now);
+ time = frame_animate_iconify_time_left(self, &now);
+
+ if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
+ /* start where the frame is supposed to be */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+ } else {
+ /* start at the icon */
+ x = iconx;
+ y = icony;
+ w = iconw;
+ h = self->size.top; /* just the titlebar */
+ }
+
+ if (time > 0) {
+ glong dx, dy, dw;
+ glong elapsed;
+
+ dx = self->area.x - iconx;
+ dy = self->area.y - icony;
+ dw = self->area.width - self->bwidth * 2 - iconw;
+ /* if restoring, we move in the opposite direction */
+ if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
+
+ elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
+ x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ h = self->size.top; /* just the titlebar */
+ }
+
+ XMoveResizeWindow(obt_display, self->window, x, y, w, h);
+ XFlush(obt_display);
+
+ return time > 0; /* repeat until we're out of time */
+}
+
+void frame_end_iconify_animation(gpointer data)
+{
+ ObFrame *self = data;
+ /* see if there is an animation going */
+ if (self->iconify_animation_going == 0) return;
+
+ if (!self->visible)
+ XUnmapWindow(obt_display, self->window);
+ else {
+ /* Send a ConfigureNotify when the animation is done, this fixes
+ KDE's pager showing the window in the wrong place. since the
+ window is mapped at a different location and is then moved, we
+ need to send the synthetic configurenotify, since apps may have
+ read the position when the client mapped, apparently. */
+ client_reconfigure(self->client, TRUE);
+ }
+
+ /* we're not animating any more ! */
+ self->iconify_animation_going = 0;
+ self->iconify_animation_timer = 0;
+
+ XMoveResizeWindow(obt_display, self->window,
+ self->area.x, self->area.y,
+ self->area.width, self->area.height);
+ /* we delay re-rendering until after we're done animating */
+ framerender_frame(self);
+ XFlush(obt_display);
+}
+
+void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
+{
+ gulong time;
+ gboolean new_anim = FALSE;
+ gboolean set_end = TRUE;
+ GTimeVal now;
+
+ /* if there is no titlebar, just don't animate for now
+ XXX it would be nice tho.. */
+ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ return;
+
+ /* get the current time */
+ g_get_current_time(&now);
+
+ /* get how long until the end */
+ time = FRAME_ANIMATE_ICONIFY_TIME;
+ if (self->iconify_animation_going) {
+ if (!!iconifying != (self->iconify_animation_going > 0)) {
+ /* animation was already going on in the opposite direction */
+ time = time - frame_animate_iconify_time_left(self, &now);
+ } else
+ /* animation was already going in the same direction */
+ set_end = FALSE;
+ } else
+ new_anim = TRUE;
+ self->iconify_animation_going = iconifying ? 1 : -1;
+
+ /* set the ending time */
+ if (set_end) {
+ self->iconify_animation_end.tv_sec = now.tv_sec;
+ self->iconify_animation_end.tv_usec = now.tv_usec;
+ g_time_val_add(&self->iconify_animation_end, time);
+ }
+
+ if (new_anim) {
+ if (self->iconify_animation_timer)
+ g_source_remove(self->iconify_animation_timer);
+ self->iconify_animation_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ FRAME_ANIMATE_ICONIFY_STEP_TIME,
+ frame_animate_iconify, self,
+ frame_end_iconify_animation);
+
+
+ /* do the first step */
+ frame_animate_iconify(self);
+
+ /* show it during the animation even if it is not "visible" */
+ if (!self->visible)
+ XMapWindow(obt_display, self->window);
+ }
+}