1 module ws.x.property; 2 3 import 4 std.algorithm, 5 std.array, 6 std.string, 7 std.conv, 8 x11.X, 9 x11.Xlib, 10 x11.Xutil, 11 x11.Xatom, 12 ws.gui.base, 13 ws.wm; 14 15 16 class BaseProperty { 17 18 x11.X.Window window; 19 Atom property; 20 string name; 21 22 abstract void update(); 23 24 } 25 26 27 class PropertyList { 28 29 private BaseProperty[] properties; 30 31 void add(BaseProperty property){ 32 properties ~= property; 33 } 34 35 void remove(BaseProperty property){ 36 properties = properties.without(property); 37 } 38 39 void remove(x11.X.Window window){ 40 properties = properties.filter!(a => a.window != window).array; 41 } 42 43 void update(XPropertyEvent* event){ 44 foreach(property; properties){ 45 if(property.property == event.atom && property.window == event.window){ 46 property.update; 47 } 48 } 49 } 50 51 void update(){ 52 foreach(property; properties){ 53 property.update; 54 } 55 } 56 57 } 58 59 mixin template PropertiesMixin(string name, string atom, int type, bool isList, Args...){ 60 mixin("Property!(type, isList) " ~ name ~ ";"); 61 static if(Args.length) 62 mixin PropertiesMixin!Args; 63 } 64 65 66 struct Properties(Args...) { 67 68 mixin PropertiesMixin!Args; 69 PropertyList propertyList; 70 71 void window(x11.X.Window window){ 72 propertyList = new PropertyList; 73 void init(string name, string atom, int type, bool isList, Args...)(){ 74 mixin(name ~ " = new Property!(type, isList)(window, atom, propertyList);"); 75 static if(Args.length) 76 init!Args; 77 } 78 init!Args; 79 } 80 81 void update(Args...)(Args args){ 82 propertyList.update(args); 83 } 84 85 } 86 87 88 class Property(ulong Format, bool List): BaseProperty { 89 90 static if(Format == XA_CARDINAL || Format == XA_PIXMAP) 91 alias Type = long; 92 static if(Format == XA_ATOM) 93 alias Type = Atom; 94 static if(Format == XA_WINDOW) 95 alias Type = x11.X.Window; 96 static if(Format == XA_STRING) 97 alias Type = string; 98 99 static if(List) 100 alias FullType = Type[]; 101 else 102 alias FullType = Type; 103 104 FullType value; 105 106 void delegate(FullType)[] handlers; 107 108 void opAssign(FullType value){ 109 this.value = value; 110 set(value); 111 } 112 113 void opOpAssign(string op)(void delegate(FullType) handler){ 114 static if(op == "~") 115 handlers ~= handler; 116 else static assert(false, op ~ "= not supported"); 117 } 118 119 alias value this; 120 121 122 this(x11.X.Window window, string name, PropertyList list = null){ 123 if(list) 124 list.add(this); 125 this.window = window; 126 this.name = name; 127 property = XInternAtom(wm.displayHandle, name.toStringz, false); 128 update; 129 } 130 131 override void update(){ 132 value = get; 133 foreach(handler; handlers) 134 handler(value); 135 } 136 137 ubyte* raw(ref ulong count){ 138 int di; 139 ulong dl; 140 ubyte* p; 141 Atom da; 142 if(XGetWindowProperty(wm.displayHandle, window, property, 0L, List || is(Type == string) ? long.max : 1, 0, is(Type == string) ? XInternAtom(wm.displayHandle, "UTF8_STRING", False) : Format, &da, &di, &count, &dl, &p) == 0 && p){ 143 return p; 144 } 145 return null; 146 } 147 148 void rawset(T1, T2)(Atom format, int size, int mode, T1* data, T2 length){ 149 XChangeProperty(wm.displayHandle, window, property, format, size, mode, cast(ubyte*)data, cast(int)length); 150 } 151 152 void request(Type[] data){ 153 XEvent e; 154 e.type = ClientMessage; 155 e.xclient.window = window; 156 e.xclient.message_type = property; 157 e.xclient.format = 32; 158 e.xclient.data.l[0..data.length] = cast(long[])data; 159 XSendEvent(wm.displayHandle, XDefaultRootWindow(wm.displayHandle), false, SubstructureNotifyMask|SubstructureRedirectMask, &e); 160 } 161 162 FullType get(){ 163 ulong count = List ? 0 : 1; 164 auto p = raw(count); 165 if(!p) 166 return FullType.init; 167 FullType value; 168 static if(List) 169 value = (cast(Type*)p)[0..count].dup; 170 else static if(is(Type == string)) 171 value = (cast(char*)p)[0..count].to!string; 172 else 173 value = *(cast(Type*)p); 174 XFree(p); 175 return value; 176 } 177 178 void set(FullType data){ 179 static if(List){ 180 rawset(Format, 32, PropModeReplace, data.ptr, data.length); 181 }else static if(is(Type == string)){ 182 rawset(XInternAtom(wm.displayHandle, "UTF8_STRING", False), 8, PropModeReplace, data.toStringz, data.length); 183 }else{ 184 rawset(Format, 32, PropModeReplace, &data, 1); 185 } 186 } 187 188 }