rumai 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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