Skip to content

Commit 8e9c6b3

Browse files
author
Vincent W. Chen
committed
Initial commit
0 parents  commit 8e9c6b3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+28467
-0
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/target
2+
/lib
3+
/classes
4+
/checkouts
5+
pom.xml
6+
pom.xml.asc
7+
*.jar
8+
*.class
9+
.lein-deps-sum
10+
.lein-failures
11+
.lein-plugins
12+
.lein-repl-history

LICENSE

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2013 Vincent W. Chen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
Except as contained in this notice, the names of the authors or their
23+
institutions shall not be used in advertising or otherwise to promote the sale,
24+
use or other dealings in this Software without prior written authorization from
25+
the authors.

project.clj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(defproject xcljb "0.1.0-SNAPSHOT"
2+
:description "X protocol Clojure-language Binding"
3+
:url "http://example.com/FIXME"
4+
:license {:name "The MIT License (MIT)"
5+
:url "http://opensource.org/licenses/MIT"}
6+
:dependencies [[org.clojure/clojure "1.5.1"]
7+
[org.clojure/data.xml "0.0.7"]
8+
[org.slf4j/slf4j-log4j12 "1.7.5"]
9+
[gloss "0.2.2-beta4"]])

src/log4j.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
log4j.rootLogger=DEBUG,console
2+
log4j.appender.console=org.apache.log4j.ConsoleAppender
3+
log4j.appender.console.layout=org.apache.log4j.PatternLayout
4+
log4j.appender.console.layout.ConversionPattern=%d %-5p %c: %m%n

src/xcljb/auth.clj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
(ns xcljb.auth
2+
(:require [xcljb.gen-common :as common]))
3+
4+
(defn- parse-xauthority []
5+
(when-let [auth-file (System/getenv "XAUTHORITY")]
6+
(with-open [ins (clojure.java.io/input-stream auth-file)
7+
ch (java.nio.channels.Channels/newChannel ins)]
8+
(let [family (common/read-bytes ch 2)
9+
addrlen (common/read-bytes ch 2)
10+
addr (common/read-string ch addrlen)
11+
numlen (common/read-bytes ch 2)
12+
num (common/read-string ch numlen)
13+
namelen (common/read-bytes ch 2)
14+
name (common/read-string ch namelen)
15+
datalen (common/read-bytes ch 2)
16+
data (doall (repeatedly datalen #(common/read-bytes ch 1)))]
17+
{:family family
18+
:addr addr
19+
:num num
20+
:name name
21+
:data data}))))
22+
23+
(defn get-auth []
24+
(parse-xauthority))

src/xcljb/conn.clj

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
(ns xcljb.conn
2+
(:require [clojure.tools.logging :as log]
3+
[gloss.core :as gcore]
4+
[gloss.io :as gio]
5+
[xcljb.auth]
6+
[xcljb.gen-common :as gen-common]
7+
[xcljb.gen.xproto-internal :as xproto-internal]
8+
[xcljb.gen.xproto-types :as xproto-types])
9+
(:import [java.net InetSocketAddress]
10+
[java.nio ByteOrder]
11+
[java.nio.channels SocketChannel]
12+
[java.util.concurrent LinkedBlockingQueue]))
13+
14+
(def ^:private PROTOCOL-MAJOR-VERSION 11)
15+
(def ^:private PROTOCOL-MINOR-VERSION 0)
16+
17+
(def ^:private BYTE-ORDER
18+
(condp = (ByteOrder/nativeOrder)
19+
ByteOrder/LITTLE_ENDIAN (int \l)
20+
ByteOrder/BIG_ENDIAN (int \B)))
21+
22+
(defn- parse-display-string [disp-str]
23+
(let [[_ display screen] (re-matches #":(\d+).?(\d*)" disp-str)]
24+
{:display (Integer/parseInt display)
25+
:screen (if (empty? screen)
26+
0
27+
(Integer/parseInt screen))}))
28+
29+
(defn- padding [n]
30+
(rem (- 4 (rem n 4))
31+
4))
32+
33+
(defn- setup-request-codec [auth-name auth-data]
34+
(let [name-len (count auth-name)
35+
data-len (count auth-data)]
36+
(gcore/compile-frame
37+
[:ubyte :byte
38+
:uint16 :uint16
39+
:uint16 :uint16 :int16
40+
(gcore/string :ascii :length name-len)
41+
(repeat (padding name-len) :byte)
42+
(repeat data-len :ubyte)
43+
(repeat (padding data-len) :byte)])))
44+
45+
(defn- make-setup-request [auth-name auth-data]
46+
(let [name-len (count auth-name)
47+
data-len (count auth-data)]
48+
(gio/contiguous
49+
(gio/encode (setup-request-codec auth-name auth-data)
50+
;; Network byte order (big-endian), since data goes through TCP.
51+
[(int \B) 0
52+
PROTOCOL-MAJOR-VERSION PROTOCOL-MINOR-VERSION
53+
name-len data-len 0
54+
auth-name
55+
(repeat (padding name-len) 0)
56+
auth-data
57+
(repeat (padding data-len) 0)]))))
58+
59+
(defn- handle-setup-failed-reply [ch]
60+
(let [reason-len (gen-common/read-bytes ch 1)
61+
protocol-major-version (gen-common/read-bytes ch 2)
62+
protocol-minor-version (gen-common/read-bytes ch 2)
63+
length (gen-common/read-bytes ch 2)
64+
reason (gen-common/read-string ch reason-len)
65+
_ (gen-common/read-pad ch (- (* length 4) reason-len))]
66+
(binding [*out* *err*]
67+
(println "Connection setup failed:" reason)
68+
(println "Protocol version: major" protocol-major-version
69+
"minor" protocol-minor-version))
70+
(System/exit 1)))
71+
72+
(defn- handle-setup-authenticate-reply [ch]
73+
(let [_ (gen-common/read-pad ch 5)
74+
length (gen-common/read-bytes ch 2)
75+
reason (gen-common/read-string ch (* length 4))]
76+
(binding [*out* *err*]
77+
(println "Connection setup requires additional authentication:" reason))
78+
(System/exit 2)))
79+
80+
(defn- get-setup-success-reply [ch]
81+
(let [_ (gen-common/read-pad ch 1)
82+
protocol-major-version (gen-common/read-bytes ch 2)
83+
protocol-minor-version (gen-common/read-bytes ch 2)
84+
length (gen-common/read-bytes ch 2)
85+
release-number (gen-common/read-bytes ch 4)
86+
resource-id-base (gen-common/read-bytes ch 4)
87+
resource-id-mask (gen-common/read-bytes ch 4)
88+
motion-buffer-size (gen-common/read-bytes ch 4)
89+
vendor-len (gen-common/read-bytes ch 2)
90+
maximum-request-length (gen-common/read-bytes ch 2)
91+
roots-len (gen-common/read-bytes ch 1)
92+
pixmap-formats-len (gen-common/read-bytes ch 1)
93+
image-byte-order (gen-common/read-bytes ch 1)
94+
bitmap-format-bit-order (gen-common/read-bytes ch 1)
95+
bitmap-format-scanline-unit (gen-common/read-bytes ch 1)
96+
bitmap-format-scanline-pad (gen-common/read-bytes ch 1)
97+
min-keycode (.read-type xproto-types/KEYCODE ch)
98+
max-keycode (.read-type xproto-types/KEYCODE ch)
99+
_ (gen-common/read-pad ch 4)
100+
vendor (gen-common/read-string ch vendor-len)
101+
pixmap-formats (doall (repeatedly pixmap-formats-len #(xproto-internal/read-FORMAT ch)))
102+
roots (doall (repeatedly roots-len #(xproto-internal/read-SCREEN ch)))]
103+
{:protocol-major-version protocol-major-version
104+
:protocol-minor-version protocol-minor-version
105+
:release-number release-number
106+
:resource-id-base resource-id-base
107+
:resource-id-mask resource-id-mask
108+
:motion-buffer-size motion-buffer-size
109+
:maximum-request-length maximum-request-length
110+
:image-byte-order image-byte-order
111+
:bitmap-format-bit-order bitmap-format-bit-order
112+
:bitmap-format-scanline-unit bitmap-format-scanline-unit
113+
:bitmap-format-scanline-pad bitmap-format-scanline-pad
114+
:min-keycode min-keycode
115+
:max-keycode max-keycode
116+
:vendor vendor
117+
:pixmap-formats pixmap-formats
118+
:roots roots}))
119+
120+
(defn- handle-setup-reply [ch]
121+
(case (gen-common/read-bytes ch 1)
122+
0 (handle-setup-failed-reply ch)
123+
1 (get-setup-success-reply ch)
124+
2 (handle-setup-authenticate-reply ch)))
125+
126+
(defn- setup [ch]
127+
(let [auth (xcljb.auth/get-auth)
128+
req (make-setup-request (:name auth) (:data auth))]
129+
(.write ch req)
130+
(handle-setup-reply ch)))
131+
132+
(defn- clear-old-replies [replyq seq-num]
133+
(when-let [{seqn :seq-num, reply :reply} (.peek replyq)]
134+
(when (< seqn seq-num)
135+
(.take replyq)
136+
(deliver reply nil)
137+
(recur replyq seq-num))))
138+
139+
(defn- deliver-reply [reply seq-num replyq]
140+
(clear-old-replies replyq seq-num)
141+
(let [{seqn :seq-num, opcode :opcode, r :reply, :as v} (.peek replyq)]
142+
(assert v "Sequence number corresponds to no request.")
143+
(assert (= seqn seq-num) "Sequence number greater than all requests.")
144+
(.take replyq)
145+
(deliver r reply)))
146+
147+
(defn- get-read-reply [replyq seq-num]
148+
(clear-old-replies replyq seq-num)
149+
(let [{seqn :seq-num, opcode :opcode, :as r} (.peek replyq)]
150+
(assert r "Sequence number corresponds to no request.")
151+
(assert (= seqn seq-num) "Sequence number greater than all requests.")
152+
(xproto-internal/read-reply opcode)))
153+
154+
(defn- handle-error [ch replyq]
155+
(let [code (gen-common/read-bytes ch 1)
156+
seq-n (gen-common/read-bytes ch 2)
157+
err ((xproto-internal/read-error code) ch)]
158+
(log/error err)
159+
(deliver-reply err seq-n replyq)))
160+
161+
(defn- handle-reply [ch replyq]
162+
(let [val (gen-common/read-bytes ch 1) ; FIXME: What if byte is signed?
163+
seq-n (gen-common/read-bytes ch 2)
164+
len (* (gen-common/read-bytes ch 4) 4)
165+
read-reply (get-read-reply replyq seq-n)
166+
reply (read-reply ch len val)]
167+
(log/debug reply)
168+
(deliver-reply reply seq-n replyq)))
169+
170+
(defn- handle-event [ch event-num replyq eventq]
171+
(let [{:keys [seq-num event]} ((xproto-internal/read-event event-num) ch)]
172+
(log/debug event)
173+
(when seq-num ; not KeymapNotify
174+
(clear-old-replies replyq seq-num))
175+
(.put eventq event)))
176+
177+
(defn- read-channel [ch replyq eventq]
178+
(while (.isOpen ch)
179+
(try
180+
(let [type-or-event (gen-common/read-bytes ch 1)]
181+
(case type-or-event
182+
;; Error.
183+
0 (handle-error ch replyq)
184+
;; Reply.
185+
1 (handle-reply ch replyq)
186+
;; Event.
187+
(handle-event ch type-or-event replyq eventq)))
188+
189+
(catch java.nio.channels.AsynchronousCloseException e
190+
(log/debug "Channel closed.")))))
191+
192+
(defn connect
193+
([] (connect "localhost" 6000))
194+
([host port]
195+
(let [ch (SocketChannel/open (InetSocketAddress. host port))
196+
;; Disable Nagle's algorithm.
197+
_ (-> ch (.socket) (.setTcpNoDelay true))
198+
_ (.finishConnect ch)
199+
setup-reply (future (setup ch))
200+
replyq (LinkedBlockingQueue.)
201+
eventq (LinkedBlockingQueue.)
202+
ch-reader (Thread. #(read-channel ch replyq eventq))]
203+
;; Wait for setup-reply to finish.
204+
@setup-reply
205+
(.start ch-reader)
206+
(atom
207+
{:conn-lock (Object.)
208+
:ch ch
209+
:setup @setup-reply
210+
:seq-num 0
211+
:res-id 0
212+
:replies replyq
213+
:events eventq
214+
:ch-reader ch-reader}))))
215+
216+
(defn disconnect [conn]
217+
(.close (:ch @conn))
218+
(swap! conn (constantly nil))
219+
nil)

src/xcljb/core.clj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(ns xcljb.core)
2+
3+
(defrecord Valueparam [masks list]
4+
xcljb.gen-common/Valueparam
5+
(to-mask [this]
6+
(reduce bit-or 0 (:masks this)))
7+
(to-list [this]
8+
(let [mask-list (map vector (:masks this) (:list this))
9+
sorted-mask-list (sort-by first mask-list)
10+
sorted-list (map second sorted-mask-list)]
11+
sorted-list)))
12+
13+
(defn get-setup [conn]
14+
(:setup @conn))
15+
16+
(defn wait-event [conn]
17+
(.take (:events @conn)))
18+
19+
(defn poll-event [conn timeout unit]
20+
(.poll (:events @conn) timeout unit))
21+
22+
(defn- max-res-id [conn]
23+
(let [res-mask (-> conn (get-setup) (:resource-id-mask))
24+
res-shifts (.getLowestSetBit (BigInteger/valueOf res-mask))]
25+
(assert (not (neg? res-shifts)))
26+
(bit-shift-right res-mask res-shifts)))
27+
28+
;;; TODO: Use XC-MISC extension to generate resource ids.
29+
(defn gen-res-id [conn]
30+
(let [res-base (-> conn (get-setup) (:resource-id-base))
31+
res-mask (-> conn (get-setup) (:resource-id-mask))
32+
res-shifts (.getLowestSetBit (BigInteger/valueOf res-mask))
33+
res-id (inc (:res-id @conn))]
34+
(assert (<= res-id (max-res-id conn)) (str "Unable to generate new resource id."))
35+
(swap! conn assoc :res-id res-id)
36+
(assert (not (neg? res-shifts)))
37+
(bit-or (bit-shift-left res-id res-shifts)
38+
res-base)))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The examples listed here are taken from http://www.x.org/releases/X11R7.7/doc/libxcb/tutorial/index.html, translated to Clojure. The chapters and sections are listed at the start of each example, so one can compare between programming in XCB and XCLJB.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
;;;; 8
2+
3+
(ns xcljb.examples.xcb-tutorial.create-window
4+
(:require [xcljb.conn :as conn]
5+
[xcljb.core :as core]
6+
[xcljb.gen.xproto :as xproto]))
7+
8+
(defn -main [& args]
9+
(let [c (conn/connect)
10+
screen (-> c (core/get-setup) (:roots) (first))
11+
win (core/gen-res-id c)]
12+
(xproto/create-window c
13+
(:CopyFromParent xproto/WindowClass)
14+
win
15+
(:root screen)
16+
0 0
17+
150 150
18+
10
19+
(:InputOutput xproto/WindowClass)
20+
(:root-visual screen)
21+
(core/->Valueparam [] []))
22+
(xproto/map-window c win)))

0 commit comments

Comments
 (0)