rumai 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/bin/rumai +1 -1
  2. data/doc/api/classes/Integer.html +7 -7
  3. data/doc/api/classes/Rumai.html +148 -141
  4. data/doc/api/classes/Rumai/Area.html +135 -130
  5. data/doc/api/classes/Rumai/Chain.html +21 -21
  6. data/doc/api/classes/Rumai/Client.html +188 -189
  7. data/doc/api/classes/Rumai/ClientContainer.html +22 -22
  8. data/doc/api/classes/Rumai/IXP/Agent.html +248 -143
  9. data/doc/api/classes/Rumai/IXP/Agent/FidStream.html +50 -50
  10. data/doc/api/classes/Rumai/IXP/Agent/MODES.html +7 -7
  11. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.html +21 -21
  12. data/doc/api/classes/Rumai/IXP/Fcall.html +33 -33
  13. data/doc/api/classes/Rumai/IXP/Qid.html +1 -1
  14. data/doc/api/classes/Rumai/IXP/Stat.html +8 -8
  15. data/doc/api/classes/Rumai/IXP/Stream.html +6 -6
  16. data/doc/api/classes/Rumai/IXP/Struct.html +34 -38
  17. data/doc/api/classes/Rumai/IXP/Struct/Field.html +42 -42
  18. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.html +14 -14
  19. data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.html +7 -7
  20. data/doc/api/classes/Rumai/IXP/Terror.html +7 -7
  21. data/doc/api/classes/Rumai/IXP/Topen.html +1 -1
  22. data/doc/api/classes/Rumai/Node.html +114 -110
  23. data/doc/api/classes/Rumai/View.html +184 -136
  24. data/doc/api/classes/String.html +15 -15
  25. data/doc/api/classes/Time.html +14 -14
  26. data/doc/api/created.rid +1 -1
  27. data/doc/api/files/lib/rumai/fs_rb.html +1 -1
  28. data/doc/api/files/lib/rumai/ixp/message_rb.html +1 -1
  29. data/doc/api/files/lib/rumai/ixp/transport_rb.html +1 -1
  30. data/doc/api/files/lib/rumai/wm_rb.html +1 -1
  31. data/doc/api/files/lib/rumai_rb.html +1 -1
  32. data/doc/api/panel/search_index.js +1 -1
  33. data/doc/history.erb +41 -0
  34. data/doc/index.html +1315 -669
  35. data/doc/setup.erb +82 -45
  36. data/doc/usage.erb +96 -132
  37. data/lib/rumai.rb +2 -2
  38. data/lib/rumai/fs.rb +11 -6
  39. data/lib/rumai/ixp/message.rb +31 -17
  40. data/lib/rumai/ixp/transport.rb +82 -36
  41. data/lib/rumai/wm.rb +87 -25
  42. metadata +5 -5
@@ -8,8 +8,8 @@ gem 'inochi', '~> 1'
8
8
  require 'inochi'
9
9
 
10
10
  Inochi.init :Rumai,
11
- :version => '3.1.0',
12
- :release => '2009-10-02',
11
+ :version => '3.2.0',
12
+ :release => '2009-11-17',
13
13
  :website => 'http://snk.tuxfamily.org/lib/rumai/',
14
14
  :tagline => 'Ruby interface to the wmii window manager',
15
15
  :develop => {
@@ -16,7 +16,7 @@ module Rumai
16
16
 
17
17
  begin
18
18
  # We use a single, global connection to wmii's IXP server.
19
- IXP_AGENT = IXP::Agent.new UNIXSocket.new(IXP_SOCK_ADDR)
19
+ IXP_AGENT = IXP::Agent.new(UNIXSocket.new(IXP_SOCK_ADDR))
20
20
 
21
21
  rescue => error
22
22
  error.message << %{
@@ -71,7 +71,11 @@ module Rumai
71
71
  # Returns the names of all files in this directory.
72
72
  #
73
73
  def entries
74
- IXP_AGENT.entries @path rescue []
74
+ begin
75
+ IXP_AGENT.entries @path
76
+ rescue IXP::Error
77
+ []
78
+ end
75
79
  end
76
80
 
77
81
  ##
@@ -205,9 +209,10 @@ module Rumai
205
209
  end
206
210
  end
207
211
 
208
- # We use extend() AFTER all methods have been defined in the class so
209
- # that the Externalize* module can do its magic. If we include()d
210
- # the module instead before all methods in the class have been
211
- # defined, then the magic would only apply to SOME of the methods!
212
+ # NOTE: We use extend() **AFTER** all methods have been defined
213
+ # in the class so that the ExportInstanceMethods module
214
+ # can do its magic. If, instead, we include()d the module
215
+ # before all methods in the class had been defined, then
216
+ # the magic would only apply to **SOME** of the methods!
212
217
  Node.extend ExportInstanceMethods
213
218
  end
@@ -66,7 +66,7 @@ module Rumai
66
66
  # Transforms this object into a string of 9P2000 bytes.
67
67
  #
68
68
  def to_9p
69
- @fields.inject('') {|s,f| s << f.to_9p(@values) }
69
+ @fields.inject([]) {|s,f| s << f.to_9p(@values) }.join
70
70
  end
71
71
 
72
72
  ##
@@ -85,7 +85,9 @@ module Rumai
85
85
  #
86
86
  def self.included target
87
87
  class << target
88
+ ##
88
89
  # Returns a list of fields which compose this Struct.
90
+ #
89
91
  def fields
90
92
  @fields ||=
91
93
  if superclass.respond_to? :fields
@@ -101,22 +103,24 @@ module Rumai
101
103
  # [args] arguments for Field.new()
102
104
  #
103
105
  def field name, format = nil, *args
104
- c = Field.factory(format)
105
- f = c.new(name.to_sym, format, *args)
106
- fields << f # register field as being part of this structure
106
+ klass = Field.factory(format)
107
+ field = klass.new(name.to_sym, format, *args)
108
+
109
+ # register field as being part of this structure
110
+ fields << field
107
111
 
108
112
  # provide accessor methods to field values
109
- self.class_eval %{
110
- def #{f.name}
111
- @values[#{f.name.inspect}]
113
+ self.class_eval <<-EOS, __FILE__, __LINE__
114
+ def #{field.name}
115
+ @values[#{field.name.inspect}]
112
116
  end
113
117
 
114
- def #{f.name}= value
115
- @values[#{f.name.inspect}] = value
118
+ def #{field.name}= value
119
+ @values[#{field.name.inspect}] = value
116
120
  end
117
- }
121
+ EOS
118
122
 
119
- return f
123
+ field
120
124
  end
121
125
 
122
126
  ##
@@ -323,7 +327,10 @@ module Rumai
323
327
  field :version , 4
324
328
  field :path , 8
325
329
 
326
- # from http://swtch.com/usr/local/plan9/include/libc.h
330
+ ##
331
+ # The following constant definitions originate from:
332
+ # http://swtch.com/usr/local/plan9/include/libc.h
333
+ #
327
334
  QTDIR = 0x80 # type bit for directories
328
335
  QTAPPEND = 0x40 # type bit for append only files
329
336
  QTEXCL = 0x20 # type bit for exclusive use files
@@ -355,7 +362,10 @@ module Rumai
355
362
  field :gid , String
356
363
  field :muid , String
357
364
 
358
- # from http://swtch.com/usr/local/plan9/include/libc.h
365
+ ##
366
+ # The following constant definitions originate from:
367
+ # http://swtch.com/usr/local/plan9/include/libc.h
368
+ #
359
369
  DMDIR = 0x80000000 # mode bit for directories
360
370
  DMAPPEND = 0x40000000 # mode bit for append only files
361
371
  DMEXCL = 0x20000000 # mode bit for exclusive use files
@@ -435,8 +445,9 @@ module Rumai
435
445
  field :msize , 4
436
446
  field :version , String
437
447
 
438
- VERSION = '9P2000'.freeze
439
- MSIZE = 8192 # magic number defined in Plan9 for [TR]version and [TR]read
448
+ VERSION = '9P2000'.freeze
449
+ MSIZE = 8192 # magic number defined in Plan9
450
+ # for [TR]version and [TR]read
440
451
  end
441
452
 
442
453
  # size[4] Rversion tag[2] msize[4] version[s]
@@ -510,7 +521,10 @@ module Rumai
510
521
  field :fid , 4
511
522
  field :mode , 1
512
523
 
513
- # from http://swtch.com/usr/local/plan9/include/libc.h
524
+ ##
525
+ # The following constant definitions originate from:
526
+ # http://swtch.com/usr/local/plan9/include/libc.h
527
+ #
514
528
  OREAD = 0 # open for read
515
529
  OWRITE = 1 # write
516
530
  ORDWR = 2 # read and write
@@ -688,7 +702,7 @@ class String
688
702
  # given 9P2000 byte stream and returns the instance.
689
703
  #
690
704
  def self.from_9p stream
691
- stream.read(stream.read_9p(2))
705
+ stream.read stream.read_9p(2)
692
706
  end
693
707
  end
694
708
 
@@ -5,30 +5,29 @@
5
5
  #++
6
6
 
7
7
  require 'rumai/ixp/message'
8
- require 'thread' # for Mutex
8
+ require 'thread' # for Mutex and Queue
9
9
 
10
10
  module Rumai
11
11
  module IXP
12
12
  ##
13
- # A thread-safe proxy that multiplexes many
13
+ # A thread-safe channel that multiplexes many
14
14
  # threads onto a single 9P2000 connection.
15
15
  #
16
+ # The send/recv implementation is based on the XCB cookie approach:
17
+ # http://www.x.org/releases/X11R7.5/doc/libxcb/tutorial/#requestsreplies
18
+ #
16
19
  class Agent
17
20
  attr_reader :msize
18
21
 
22
+ ##
23
+ # [stream]
24
+ # I/O stream on which a 9P2000 server is listening.
25
+ #
19
26
  def initialize stream
20
- @stream = stream
21
- @send_lock = Mutex.new
22
- @recv_bays = Hash.new {|h,k| h[k] = Queue.new } # tag => Queue(message)
23
-
24
- # background thread which continuously receives
25
- # and dispatches messages from the 9P2000 server
26
- Thread.new do
27
- while true
28
- msg = Fcall.from_9p @stream
29
- @recv_bays[msg.tag] << msg
30
- end
31
- end.priority = -1
27
+ @stream = stream
28
+
29
+ @recv_buf = {} # tag => message
30
+ @recv_lock = Mutex.new
32
31
 
33
32
  @tag_pool = RangedPool.new(0...BYTE2_MASK)
34
33
  @fid_pool = RangedPool.new(0...BYTE4_MASK)
@@ -71,7 +70,9 @@ module Rumai
71
70
  @pool = Queue.new
72
71
  end
73
72
 
73
+ ##
74
74
  # Returns an unoccupied range member from the pool.
75
+ #
75
76
  def obtain
76
77
  begin
77
78
  @pool.deq true
@@ -94,38 +95,79 @@ module Rumai
94
95
  end
95
96
  end
96
97
 
98
+ ##
97
99
  # Marks the given member as being unoccupied so
98
100
  # that it may be occupied again in the future.
101
+ #
99
102
  def release member
100
103
  @pool << member
101
104
  end
102
105
  end
103
106
 
104
107
  ##
105
- # Sends the given message (Rumai::IXP::Fcall) and returns its response.
108
+ # Sends the given request (Rumai::IXP::Fcall) and returns
109
+ # a ticket that you can use later to receive the reply.
106
110
  #
107
- # This method allows you to perform a 9P2000 transaction without
108
- # worrying about the details of tag collisions and thread safety.
109
- #
110
- def talk request
111
- # send the request
111
+ def send request
112
112
  tag = @tag_pool.obtain
113
- bay = @recv_bays[tag]
114
113
 
115
114
  request.tag = tag
116
- output = request.to_9p
117
- @send_lock.synchronize do
118
- @stream << output
115
+ @stream << request.to_9p
116
+
117
+ tag
118
+ end
119
+
120
+ ##
121
+ # Returns the reply for the given ticket, which was previously given
122
+ # to you when you sent the corresponding request (Rumai::IXP::Fcall).
123
+ #
124
+ def recv tag
125
+ loop do
126
+ reply = @recv_lock.synchronize do
127
+ if @recv_buf.key? tag
128
+ @recv_buf.delete tag
129
+ else
130
+ # reply was not in the receive buffer, so wait
131
+ # for the next reply... hoping that it is ours
132
+ msg = Fcall.from_9p(@stream)
133
+
134
+ if msg.tag == tag
135
+ msg
136
+ else
137
+ # we got someone else's reply, so buffer
138
+ # it (for them to receive) and try again
139
+ @recv_buf[msg.tag] = msg
140
+ nil
141
+ end
142
+ end
143
+ end
144
+
145
+ if reply
146
+ @tag_pool.release tag
147
+
148
+ if reply.is_a? Rerror
149
+ raise Error, reply.ename
150
+ end
151
+
152
+ return reply
153
+ else
154
+ # give other threads a chance to receive
155
+ Thread.pass
156
+ end
119
157
  end
158
+ end
120
159
 
121
- # receive the response
122
- response = bay.shift
123
- @tag_pool.release tag
160
+ ##
161
+ # Sends the given request (Rumai::IXP::Fcall) and returns its reply.
162
+ #
163
+ def talk request
164
+ tag = send(request)
124
165
 
125
- if response.is_a? Rerror
126
- raise Error, "#{response.ename.inspect} in response to #{request.inspect}"
127
- else
128
- return response
166
+ begin
167
+ recv tag
168
+ rescue Error => e
169
+ e.message << " -- in reply to #{request.inspect}"
170
+ raise
129
171
  end
130
172
  end
131
173
 
@@ -182,7 +224,7 @@ module Rumai
182
224
  ##
183
225
  # Encapsulates I/O access over a file handle (fid).
184
226
  #
185
- # NOTE: this class is NOT thread-safe.
227
+ # NOTE: this class is NOT thread safe!
186
228
  #
187
229
  class FidStream
188
230
  attr_reader :fid, :stat
@@ -276,8 +318,8 @@ module Rumai
276
318
  # Writes the given content at the current position in this stream.
277
319
  #
278
320
  def write content
279
- raise 'closed streams cannot be written to' if @closed
280
- raise 'directories cannot be written to' if @stat.directory?
321
+ raise 'cannot write to a closed stream' if @closed
322
+ raise 'cannot write to a directory' if @stat.directory?
281
323
 
282
324
  data = content.to_s
283
325
  limit = data.length + @pos
@@ -310,7 +352,10 @@ module Rumai
310
352
  end
311
353
 
312
354
  ##
313
- # Returns the names of all files inside the directory whose path is given.
355
+ # Returns the basenames of all files
356
+ # inside the directory at the given path.
357
+ #
358
+ # See Dir::entries in the Ruby documentation.
314
359
  #
315
360
  def entries path
316
361
  unless stat(path).directory?
@@ -321,7 +366,8 @@ module Rumai
321
366
  end
322
367
 
323
368
  ##
324
- # Returns the content of the file/directory at the given path.
369
+ # Writes the given content to
370
+ # the file at the given path.
325
371
  #
326
372
  def write path, content
327
373
  open path, 'w' do |f|
@@ -21,7 +21,9 @@ module Rumai
21
21
  CLIENT_GROUPING_TAG = '@'.freeze
22
22
  CLIENT_STICKY_TAG = '/./'.freeze
23
23
 
24
+ #-----------------------------------------------------------------------------
24
25
  # abstraction of WM components
26
+ #-----------------------------------------------------------------------------
25
27
 
26
28
  ##
27
29
  # NOTE: Inheritors must override the 'chain' method.
@@ -124,7 +126,9 @@ module Rumai
124
126
  View.curr.clients
125
127
  end
126
128
 
129
+ #-------------------------------------------------------------------------
127
130
  # WM operations
131
+ #-------------------------------------------------------------------------
128
132
 
129
133
  ##
130
134
  # Focuses this client within the given view.
@@ -145,7 +149,6 @@ module Rumai
145
149
  direction = src < dst ? :down : :up
146
150
 
147
151
  distance.times { v.select direction }
148
-
149
152
  break
150
153
  end
151
154
  end
@@ -288,7 +291,9 @@ module Rumai
288
291
 
289
292
  alias manage! float!
290
293
 
294
+ #-------------------------------------------------------------------------
291
295
  # WM hierarchy
296
+ #-------------------------------------------------------------------------
292
297
 
293
298
  ##
294
299
  # Returns the area that contains this client within the given view.
@@ -304,7 +309,9 @@ module Rumai
304
309
  tags.map! {|t| View.new t }
305
310
  end
306
311
 
312
+ #-------------------------------------------------------------------------
307
313
  # tag manipulations
314
+ #-------------------------------------------------------------------------
308
315
 
309
316
  TAG_DELIMITER = '+'.freeze
310
317
 
@@ -388,7 +395,9 @@ module Rumai
388
395
  end
389
396
  end
390
397
 
398
+ #-------------------------------------------------------------------------
391
399
  # multiple client grouping
400
+ #-------------------------------------------------------------------------
392
401
 
393
402
  ##
394
403
  # Checks if this client is included in the current grouping.
@@ -559,10 +568,17 @@ module Rumai
559
568
  # Sets the layout of clients in this column.
560
569
  #
561
570
  def layout= mode
571
+ case mode
572
+ when :stack then mode = 'stack-max'
573
+ when :max then mode = 'stack+max'
574
+ end
575
+
562
576
  @view.ctl.write "colmode #{@id} #{mode}"
563
577
  end
564
578
 
579
+ #-------------------------------------------------------------------------
565
580
  # WM operations
581
+ #-------------------------------------------------------------------------
566
582
 
567
583
  ##
568
584
  # Puts focus on this area.
@@ -571,7 +587,9 @@ module Rumai
571
587
  @view.ctl.write "select #{@id}"
572
588
  end
573
589
 
590
+ #-------------------------------------------------------------------------
574
591
  # array abstraction: area is an array of clients
592
+ #-------------------------------------------------------------------------
575
593
 
576
594
  ##
577
595
  # Returns the number of clients in this area.
@@ -590,7 +608,7 @@ module Rumai
590
608
  insert clients
591
609
 
592
610
  # move inserted clients to bottom
593
- clients.each_with_index do |c, i|
611
+ clients.reverse.each_with_index do |c, i|
594
612
  until c.id == self.client_ids[-i.succ]
595
613
  c.send :down
596
614
  end
@@ -743,7 +761,9 @@ module Rumai
743
761
  super view_id, '/tag'
744
762
  end
745
763
 
764
+ #-------------------------------------------------------------------------
746
765
  # WM operations
766
+ #-------------------------------------------------------------------------
747
767
 
748
768
  ##
749
769
  # Returns the manifest of all areas and clients in this view.
@@ -759,7 +779,9 @@ module Rumai
759
779
  ctl.write "select #{direction}"
760
780
  end
761
781
 
782
+ #-------------------------------------------------------------------------
762
783
  # WM hierarchy
784
+ #-------------------------------------------------------------------------
763
785
 
764
786
  ##
765
787
  # Returns the area which contains the given client in this view.
@@ -830,7 +852,9 @@ module Rumai
830
852
 
831
853
  alias each_managed_area each_column
832
854
 
855
+ #-------------------------------------------------------------------------
833
856
  # visual arrangement of clients
857
+ #-------------------------------------------------------------------------
834
858
 
835
859
  ##
836
860
  # Arranges the clients in this view, while maintaining
@@ -848,13 +872,7 @@ module Rumai
848
872
  main.layout = :default
849
873
 
850
874
  # collapse remaining areas into secondary column
851
- extra = columns[1..-1]
852
-
853
- if extra.length > 1
854
- extra.reverse.each_cons(2) do |src, dst|
855
- dst.concat src
856
- end
857
- end
875
+ extra = squeeze_columns(1..-1)
858
876
 
859
877
  if dock = extra.first
860
878
  dock.layout = :default
@@ -887,6 +905,31 @@ module Rumai
887
905
  end
888
906
  end
889
907
 
908
+ ##
909
+ # Arranges the clients in this view, while maintaining
910
+ # their relative order, in the given number of columns.
911
+ #
912
+ def arrange_in_stacks num_stacks
913
+ return if num_stacks < 1
914
+
915
+ # compute client distribution
916
+ num_clients = num_managed_clients
917
+ return unless num_clients > 0
918
+
919
+ stack_length = num_clients / num_stacks
920
+ return if stack_length < 1
921
+
922
+ # apply the distribution
923
+ maintain_focus do
924
+ each_column do |a|
925
+ a.length = stack_length
926
+ a.layout = :stack
927
+ end
928
+
929
+ squeeze_columns num_stacks-1..-1
930
+ end
931
+ end
932
+
890
933
  ##
891
934
  # Arranges the clients in this view, while
892
935
  # maintaining their relative order, in a (at
@@ -934,15 +977,30 @@ module Rumai
934
977
 
935
978
  private
936
979
 
980
+ ##
981
+ # Squeezes all columns in the given index range into a single one.
982
+ #
983
+ def squeeze_columns range
984
+ extra = columns[range]
985
+
986
+ if extra.length > 1
987
+ extra.reverse.each_cons(2) do |src, dst|
988
+ dst.concat src
989
+ end
990
+ end
991
+
992
+ extra
993
+ end
994
+
937
995
  ##
938
996
  # Executes the given block and restores
939
997
  # focus to the client that had focus
940
998
  # before the given block was executed.
941
999
  #
942
1000
  def maintain_focus
943
- c = Client.curr
1001
+ c, v = Client.curr, View.curr
944
1002
  yield
945
- c.focus
1003
+ c.focus v
946
1004
  end
947
1005
 
948
1006
  ##
@@ -953,7 +1011,9 @@ module Rumai
953
1011
  end
954
1012
  end
955
1013
 
1014
+ #-----------------------------------------------------------------------------
956
1015
  # access to global WM state
1016
+ #-----------------------------------------------------------------------------
957
1017
 
958
1018
  ##
959
1019
  # Returns the root of IXP file system hierarchy.
@@ -989,6 +1049,22 @@ module Rumai
989
1049
  ary
990
1050
  end
991
1051
 
1052
+ ##
1053
+ # Returns a list of all grouped clients in
1054
+ # the currently focused view. If there are
1055
+ # no grouped clients, then the currently
1056
+ # focused client is returned in the list.
1057
+ #
1058
+ def grouping
1059
+ list = curr_view.clients.select {|c| c.group? }
1060
+ list << curr_client if list.empty? and curr_client.exist?
1061
+ list
1062
+ end
1063
+
1064
+ #-----------------------------------------------------------------------------
1065
+ # shortcuts for interactive WM manipulation (via IRB)
1066
+ #-----------------------------------------------------------------------------
1067
+
992
1068
  def curr_client ; Client.curr ; end
993
1069
  def next_client ; curr_client.next ; end
994
1070
  def prev_client ; curr_client.prev ; end
@@ -1005,20 +1081,6 @@ module Rumai
1005
1081
  def next_tag ; next_view.id ; end
1006
1082
  def prev_tag ; prev_view.id ; end
1007
1083
 
1008
- ##
1009
- # Returns a list of all grouped clients in
1010
- # the currently focused view. If there are
1011
- # no grouped clients, then the currently
1012
- # focused client is returned in the list.
1013
- #
1014
- def grouping
1015
- list = curr_view.clients.select {|c| c.group? }
1016
- list << curr_client if list.empty? and curr_client.exist?
1017
- list
1018
- end
1019
-
1020
- # shortcuts for interactive WM manipulation (via IRB)
1021
-
1022
1084
  # provide easy access to container state information
1023
1085
  [Client, Area, View].each {|c| c.extend ExportInstanceMethods }
1024
1086