├── code ├── xmp │ ├── .gitignore │ ├── v4 │ │ ├── libxmp.deps │ │ ├── Makefile │ │ ├── xmptest.vala │ │ └── libxmp.vapi │ ├── v5 │ │ ├── libxmp.deps │ │ ├── Makefile │ │ ├── xmptest.vala │ │ └── libxmp.vapi │ ├── .DS_Store │ ├── v1 │ │ ├── .DS_Store │ │ ├── xmptest.vala │ │ ├── libxmp.vapi │ │ └── Makefile │ ├── v2 │ │ ├── .DS_Store │ │ ├── xmptest.vala │ │ ├── Makefile │ │ └── libxmp.vapi │ ├── v3 │ │ ├── .DS_Store │ │ ├── Makefile │ │ ├── xmptest.vala │ │ └── libxmp.vapi │ └── Makefile ├── .gitignore ├── netradio │ ├── .gitignore │ ├── imageModel.vala │ ├── fontAwesomeButton.vala │ ├── de.vanille.NetRadio.xml │ ├── netRadio.vala │ ├── streamModel.vala │ ├── genreModel.vala │ ├── dbusInterface.vala │ ├── Makefile │ ├── stationModel.vala │ ├── dbusServer.vala │ ├── netradio-client.vala │ ├── DirectoryClient.vala │ ├── directoryClient.vala │ ├── playerController.vala │ └── mainWindow.vala ├── helloWorld.vala ├── ipifyClient.vala ├── helloObjectWorld.vala ├── helloGUIWorld.vala ├── helloObjectWorld2.vala ├── jsonParser.vala ├── meson.build ├── inotifyWatcher.vala ├── weatherClient.vala ├── ntpClient.vala └── gusb │ └── gusb.vapi └── README.md /code/xmp/.gitignore: -------------------------------------------------------------------------------- 1 | xmptest 2 | -------------------------------------------------------------------------------- /code/xmp/v4/libxmp.deps: -------------------------------------------------------------------------------- 1 | posix 2 | 3 | -------------------------------------------------------------------------------- /code/xmp/v5/libxmp.deps: -------------------------------------------------------------------------------- 1 | posix 2 | 3 | -------------------------------------------------------------------------------- /code/.gitignore: -------------------------------------------------------------------------------- 1 | *.c 2 | *.o 3 | *.dSYM 4 | .DS_* 5 | -------------------------------------------------------------------------------- /code/netradio/.gitignore: -------------------------------------------------------------------------------- 1 | netradio 2 | netradio2 3 | netradio-client 4 | -------------------------------------------------------------------------------- /code/xmp/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mickeyl/introduction-to-vala/HEAD/code/xmp/.DS_Store -------------------------------------------------------------------------------- /code/xmp/v1/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mickeyl/introduction-to-vala/HEAD/code/xmp/v1/.DS_Store -------------------------------------------------------------------------------- /code/xmp/v2/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mickeyl/introduction-to-vala/HEAD/code/xmp/v2/.DS_Store -------------------------------------------------------------------------------- /code/xmp/v3/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mickeyl/introduction-to-vala/HEAD/code/xmp/v3/.DS_Store -------------------------------------------------------------------------------- /code/helloWorld.vala: -------------------------------------------------------------------------------- 1 | // helloWorld.vala 2 | 3 | int main( string[] args ) 4 | { 5 | stdout.printf( "Hello World!\n" ); 6 | return 0; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /code/xmp/v1/xmptest.vala: -------------------------------------------------------------------------------- 1 | int main( string[] args ) 2 | { 3 | print( "Using XMP version %s (%u)\n", Xmp.version, Xmp.vercode ); 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # introduction-to-vala 2 | Examples for the book "Introduction to Vala Programming" by Dr. Michael 'Mickey' Lauer 3 | 4 | The book is unfortunately not available right now, due to a 2nd edition being prepared in cooperation with a publisher. 5 | -------------------------------------------------------------------------------- /code/xmp/v1/libxmp.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "XMP_", lower_case_cprefix = "xmp_", cheader_filename = "xmp.h")] 2 | namespace Xmp 3 | { 4 | [CCode (cname = "xmp_version")] 5 | public const string version; 6 | [CCode (cname = "xmp_vercode")] 7 | public const uint vercode; 8 | } 9 | -------------------------------------------------------------------------------- /code/xmp/v2/xmptest.vala: -------------------------------------------------------------------------------- 1 | 2 | int main( string[] args ) 3 | { 4 | print( "Using XMP version %s (%u)\n", Xmp.version, Xmp.vercode ); 5 | foreach ( var format in Xmp.get_format_list() ) 6 | { 7 | print( "XMP recognizes format '%s'\n", format ); 8 | } 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /code/xmp/v1/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/xmp/v2/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/xmp/v3/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/xmp/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi libxmp.deps 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/xmp/v4/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi libxmp.deps 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/xmp/v5/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | VAPIFILES = libxmp.vapi libxmp.deps 4 | SOURCES = xmptest.vala 5 | 6 | DEPS = \ 7 | --vapidir=. \ 8 | --pkg=libxmp 9 | 10 | .PHONY: clean 11 | 12 | xmptest: $(SOURCES) $(VAPIFILES) 13 | $(VALA) -o $@ --save-temps $(DEPS) $(SOURCES) 14 | 15 | all: xmptest 16 | 17 | clean: 18 | rm -rf xmptest *.c *.o *.dSYM 19 | 20 | -------------------------------------------------------------------------------- /code/ipifyClient.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala 2 | 3 | void main() 4 | { 5 | var url = "https://api.ipify.org"; 6 | var session = new Soup.Session(); 7 | var message = new Soup.Message( "GET", url ); 8 | session.send_message( message ); 9 | var body = (string) message.response_body.data; 10 | print( @"My public IP address is '$body'\n" ); 11 | } 12 | -------------------------------------------------------------------------------- /code/xmp/v2/libxmp.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "XMP_", lower_case_cprefix = "xmp_", cheader_filename = "xmp.h")] 2 | namespace Xmp 3 | { 4 | [CCode (cname = "xmp_version")] 5 | public const string version; 6 | [CCode (cname = "xmp_vercode")] 7 | public const uint vercode; 8 | 9 | [CCode (array_null_terminated = true)] 10 | public unowned string[] get_format_list(); 11 | } 12 | -------------------------------------------------------------------------------- /code/helloObjectWorld.vala: -------------------------------------------------------------------------------- 1 | // helloObjectWorld.vala 2 | 3 | class HelloWorld 4 | { 5 | private string name; 6 | 7 | public HelloWorld( string name ) 8 | { 9 | this.name = name; 10 | } 11 | 12 | public void greet() 13 | { 14 | var fullGreeting = "Hello " + this.name + "!\n"; 15 | stdout.printf( fullGreeting ); 16 | } 17 | } 18 | 19 | int main( string[] args ) 20 | { 21 | var helloWorldObject = new HelloWorld( args[1] ); 22 | helloWorldObject.greet(); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /code/xmp/v4/xmptest.vala: -------------------------------------------------------------------------------- 1 | 2 | int main( string[] args ) 3 | { 4 | if ( args.length < 2 ) 5 | { 6 | print( "Usage: %s \n", args[0] ); 7 | return -1; 8 | } 9 | 10 | var player = new Xmp.Player(); 11 | var valid = player.load_module( args[1] ); 12 | if ( valid < 0 ) 13 | { 14 | print( "Can't recognize module '%s'\n", args[1] ); 15 | return -1; 16 | } 17 | 18 | print( "Module %s successfully loaded\n", args[1] ); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /code/xmp/v3/xmptest.vala: -------------------------------------------------------------------------------- 1 | 2 | int main( string[] args ) 3 | { 4 | if ( args.length < 2 ) 5 | { 6 | print( "Usage: %s \n", args[0] ); 7 | return -1; 8 | } 9 | 10 | Xmp.TestInfo info; 11 | var valid = Xmp.test_module( args[1], out info ); 12 | 13 | if ( valid != 0 ) 14 | { 15 | print( "Can't recognize module '%s'\n", args[1] ); 16 | return -1; 17 | } 18 | 19 | print( "Valid '%s' module: '%s'\n", info.type, info.name ); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /code/netradio/imageModel.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | public class ImageModel : Object 5 | { 6 | public string url { get; private set; } 7 | public string thumb { get; private set; } 8 | 9 | public ImageModel.fromJsonObject( Json.Object json ) 10 | { 11 | _url = json.get_string_member( "url" ); 12 | 13 | var thumb = json.get_object_member( "thumb" ); 14 | _thumb = thumb.get_string_member( "url" ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/helloGUIWorld.vala: -------------------------------------------------------------------------------- 1 | int main( string[] args ) 2 | { 3 | Gtk.init( ref args ); 4 | 5 | var window = new Gtk.Window(); 6 | window.title = "Hello UI World"; 7 | 8 | window.border_width = 10; 9 | window.window_position = Gtk.WindowPosition.CENTER; 10 | window.set_default_size( 400, 150 ); 11 | window.destroy.connect( Gtk.main_quit ); 12 | 13 | var button = new Gtk.Button.with_label( "Click me!" ); 14 | button.clicked.connect( () => { 15 | button.label = "Thank you!"; 16 | } ); 17 | 18 | window.add( button ); 19 | window.show_all(); 20 | 21 | Gtk.main(); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /code/helloObjectWorld2.vala: -------------------------------------------------------------------------------- 1 | // helloObjectWorld.vala 2 | 3 | class HelloWorld 4 | { 5 | private string name; 6 | 7 | public HelloWorld( string name ) 8 | { 9 | this.name = name; 10 | } 11 | 12 | public void greet() 13 | { 14 | var fullGreeting = "Hello " + this.name + "!\n"; 15 | stdout.printf( fullGreeting ); 16 | } 17 | } 18 | 19 | int main( string[] args ) 20 | { 21 | if ( args.length < 2 ) 22 | { 23 | stderr.printf( "Usage: %s \n", args[0] ); 24 | return -1; 25 | } 26 | var helloWorldObject = new HelloWorld( args[1] ); 27 | helloWorldObject.greet(); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /code/xmp/v3/libxmp.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "XMP_", lower_case_cprefix = "xmp_")] 2 | namespace Xmp 3 | { 4 | [CCode (cname = "xmp_version")] 5 | public const string version; 6 | [CCode (cname = "xmp_vercode")] 7 | public const uint vercode; 8 | 9 | [CCode (array_null_terminated = true)] 10 | public unowned string[] get_format_list(); 11 | 12 | [CCode (cname = "struct xmp_test_info", cheader_filename = "xmp.h", destroy_function = "", has_type_id = false)] 13 | public struct TestInfo 14 | { 15 | public string name; 16 | public string type; 17 | } 18 | 19 | public static int test_module( string path, out TestInfo info ); 20 | } 21 | -------------------------------------------------------------------------------- /code/netradio/fontAwesomeButton.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | class FontAwesomeButton : Gtk.Button 5 | { 6 | private string _icon; 7 | private uint _size; 8 | 9 | public FontAwesomeButton( uint size, string icon ) 10 | { 11 | Object( label: "dummy" ); 12 | _size = size; 13 | this.icon = icon; 14 | } 15 | 16 | public string icon 17 | { 18 | get 19 | { 20 | return _icon; 21 | } 22 | set 23 | { 24 | var label = (Gtk.Label) get_child(); 25 | var markup = @"$value"; 26 | label.set_markup( markup ); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /code/netradio/de.vanille.NetRadio.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /code/netradio/netRadio.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala --pkg=gtk+-3.0 2 | 3 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 4 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 5 | 6 | class NetRadio : Gtk.Application 7 | { 8 | public NetRadio() 9 | { 10 | Object( application_id: "de.vanille.valabook.NetRadio" ); 11 | } 12 | 13 | protected override void activate() 14 | { 15 | Gtk.ApplicationWindow window = new MainWindow( this, "NetRadio – Streaming Internet Radio!" ); 16 | window.set_border_width( 10 ); 17 | window.set_default_size( 500, 800 ); 18 | window.show_all(); 19 | 20 | new DBusServer(); 21 | } 22 | } 23 | 24 | int main( string[] args ) 25 | { 26 | var app = new NetRadio(); 27 | var returnCode = app.run( args ); 28 | return returnCode; 29 | } 30 | -------------------------------------------------------------------------------- /code/netradio/streamModel.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | public class StreamModel : Object 5 | { 6 | public int listeners { get; private set; } 7 | public int status { get; private set; } 8 | public int bitrate { get; private set; } 9 | public string content_type { get; private set; } 10 | public string stream { get; private set; } 11 | 12 | public StreamModel.fromJsonObject( Json.Object json ) 13 | { 14 | _bitrate = (int) json.get_int_member( "bitrate" ); 15 | _listeners = (int) json.get_int_member( "listeners" ); 16 | _status = (int) json.get_int_member( "status" ); 17 | _content_type = json.get_string_member( "content_type" ); 18 | _stream = json.get_string_member( "stream" ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/jsonParser.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala 2 | 3 | void main() 4 | { 5 | var buffer = new uint8[5000]; 6 | var numRead = stdin.read( buffer, 5000 ); 7 | var jsonString = (string)buffer; 8 | 9 | try 10 | { 11 | var root = Json.from_string( jsonString ); 12 | var rootObject = root.get_object(); 13 | var currentObject = rootObject.get_object_member( "current" ); 14 | var conditionObject = currentObject.get_object_member( "condition" ); 15 | var conditionText = conditionObject.get_string_member( "text" ); 16 | var feelsLikeC = currentObject.get_double_member( "feelslike_c" ); 17 | var tempC = currentObject.get_double_member( "temp_c" ); 18 | 19 | print( "Current condition is '%s' with %.1f°C, feeling like %.1f°C\n", conditionText, tempC, feelsLikeC ); 20 | } 21 | catch ( Error e ) 22 | { 23 | error( @"$(e.message)" ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /code/xmp/v5/xmptest.vala: -------------------------------------------------------------------------------- 1 | 2 | int main( string[] args ) 3 | { 4 | if ( args.length < 2 ) 5 | { 6 | print( "Usage: %s \n", args[0] ); 7 | return -1; 8 | } 9 | 10 | var player = new Xmp.Player(); 11 | var valid = player.load_module( args[1] ); 12 | if ( valid < 0 ) 13 | { 14 | print( "Can't recognize module '%s'\n", args[1] ); 15 | return -1; 16 | } 17 | print( "Module %s successfully loaded\n", args[1] ); 18 | 19 | var ok = player.start(); 20 | if ( ok < 0 ) 21 | { 22 | print( "Can't start playing\n" ); 23 | return -1; 24 | } 25 | 26 | while ( player.play_frame() == 0 ) 27 | { 28 | Xmp.FrameInfo info; 29 | player.get_frame_info( out info ); 30 | if ( info.loop_count > 0 ) 31 | { 32 | break; 33 | } 34 | Posix.write( stdout.fileno(), info.buffer, info.buffer_size ); 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /code/netradio/genreModel.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | public class GenreModel : Object 5 | { 6 | public int id { get; private set; } 7 | public string title { get; private set; } 8 | public string description { get; private set; } 9 | public string slug { get; private set; } 10 | public string ancestry { get; private set; } 11 | 12 | public GenreModel( int id, string title = "", string description = "", string ancestry = "" ) 13 | { 14 | _id = id; 15 | _title = title; 16 | _description = description; 17 | _ancestry = ancestry; 18 | } 19 | 20 | public GenreModel.fromJsonObject( Json.Object json ) 21 | { 22 | _id = (int) json.get_int_member( "id" ); 23 | _title = json.get_string_member( "name" ); 24 | 25 | var count = json.get_int_member( "count" ); 26 | _description = @"$count stations in genre"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /code/netradio/dbusInterface.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | public struct Item 5 | { 6 | public int id; 7 | public string name; 8 | } 9 | 10 | [DBus (name = "de.vanille.NetRadio")] 11 | public errordomain NetRadioError 12 | { 13 | [DBus (name = "InvalidGenre")] 14 | INVALID_GENRE, 15 | [DBus (name = "InvalidStation")] 16 | INVALID_STATION 17 | } 18 | 19 | [DBus (name = "de.vanille.NetRadio")] 20 | public interface NetRadioDBusInterface : Object 21 | { 22 | [DBus (name = "ListGenres")] 23 | public abstract async Item[] listGenres() throws DBusError, IOError; 24 | 25 | [DBus (name = "ListStations")] 26 | public abstract async Item[] listStations( int id ) throws NetRadioError, DBusError, IOError; 27 | 28 | [DBus (name = "PlayStation")] 29 | public abstract async void playStation( int genre, int station ) throws NetRadioError, DBusError, IOError; 30 | 31 | [DBus (name = "Stop")] 32 | public abstract async void stop() throws DBusError, IOError; 33 | } 34 | -------------------------------------------------------------------------------- /code/meson.build: -------------------------------------------------------------------------------- 1 | project ( 2 | 'introduction-to-vala', 3 | 'c', 'vala', 4 | version: '0.1.0', 5 | ) 6 | 7 | dependencies = [ 8 | dependency ('glib-2.0'), 9 | dependency ('gobject-2.0'), 10 | dependency ('libsoup-2.4'), 11 | dependency ('json-glib-1.0'), 12 | dependency ('gstreamer-player-1.0'), 13 | dependency ('gio-2.0'), 14 | dependency ('gtk+-3.0'), 15 | ] 16 | 17 | net_radio_sources = files ( 18 | 'netradio/netRadio.vala', 19 | 'netradio/mainWindow.vala', 20 | 'netradio/fontAwesomeButton.vala', 21 | 'netradio/stationModel.vala', 22 | 'netradio/genreModel.vala', 23 | 'netradio/streamModel.vala', 24 | 'netradio/imageModel.vala', 25 | 'netradio/directoryClient.vala', 26 | 'netradio/playerController.vala', 27 | 'netradio/dbusInterface.vala', 28 | 'netradio/dbusServer.vala', 29 | ) 30 | 31 | client_sources = files ( 32 | 'netradio/netradio-client.vala', 33 | 'netradio/dbusInterface.vala', 34 | ) 35 | 36 | executable('netradio', net_radio_sources, dependencies: dependencies) 37 | executable ('netradio-client', client_sources, dependencies: dependencies) 38 | -------------------------------------------------------------------------------- /code/xmp/v4/libxmp.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "XMP_", lower_case_cprefix = "xmp_", cheader_filename = "xmp.h")] 2 | namespace Xmp 3 | { 4 | [CCode (cname = "xmp_version")] 5 | public const string version; 6 | [CCode (cname = "xmp_vercode")] 7 | public const uint vercode; 8 | 9 | [CCode (array_null_terminated = true)] 10 | public unowned string[] get_format_list(); 11 | 12 | [CCode (cname = "struct xmp_test_info", cheader_filename = "xmp.h", destroy_function = "", has_type_id = false)] 13 | public struct TestInfo 14 | { 15 | public string name; 16 | public string type; 17 | } 18 | public static int test_module( string path, out TestInfo info ); 19 | 20 | [Compact] 21 | [CCode (cname = "void", cheader_filename = "xmp.h", free_function = "xmp_free_context")] 22 | public class Player 23 | { 24 | [CCode (cname = "xmp_create_context")] 25 | public Player(); 26 | 27 | [CCode (cname = "xmp_load_module")] 28 | public int load_module( string path ); 29 | [CCode (cname = "xmp_load_module_from_memory")] 30 | public int load_module_from_memory( uint8[] buffer ); 31 | [CCode (cname = "xmp_load_module_from_file")] 32 | public int load_module_from_file( Posix.FILE file, long size ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /code/netradio/Makefile: -------------------------------------------------------------------------------- 1 | VALA = valac 2 | 3 | SOURCES = \ 4 | netRadio.vala \ 5 | mainWindow.vala \ 6 | fontAwesomeButton.vala \ 7 | \ 8 | stationModel.vala \ 9 | genreModel.vala \ 10 | streamModel.vala \ 11 | imageModel.vala \ 12 | \ 13 | directoryClient.vala \ 14 | playerController.vala \ 15 | \ 16 | dbusInterface.vala \ 17 | dbusServer.vala 18 | 19 | CSOURCES = \ 20 | netRadio.c \ 21 | mainWindow.c \ 22 | stationModel.c \ 23 | genreModel.c \ 24 | streamModel.c \ 25 | imageModel.c \ 26 | directoryClient.c \ 27 | fontAwesomeButton.c \ 28 | playerController.c \ 29 | dbusInterface.c \ 30 | dbusServer.c 31 | 32 | DEPS = \ 33 | --pkg=gtk+-3.0 \ 34 | --pkg=json-glib-1.0 \ 35 | --pkg=libsoup-2.4 \ 36 | --pkg=gstreamer-player-1.0 \ 37 | --pkg=gio-2.0 38 | 39 | .PHONY: clean 40 | 41 | netradio: $(SOURCES) 42 | $(VALA) -g -o $@ --save-temps $(DEPS) $(SOURCES) 43 | 44 | netradio2: $(CSOURCES) 45 | gcc -g `pkg-config --cflags --libs gtk+-3.0 json-glib-1.0 libsoup-2.4 gstreamer-player-1.0 gio-2.0` -O0 $(CSOURCES) -o $@ 46 | 47 | netradio-client: netradio-client.vala 48 | $(VALA) -g -o $@ --save-temps --pkg=gio-2.0 dbusInterface.vala netradio-client.vala 49 | 50 | all: netradio netradio2 netradio-client 51 | 52 | clean: 53 | rm -rf netradio netradio2 netradio-client *.c *.o *.dSYM 54 | 55 | -------------------------------------------------------------------------------- /code/netradio/stationModel.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | public class StationModel : Object 5 | { 6 | public int id { get; private set; } 7 | public string name { get; private set; } 8 | public string description { get; private set; } 9 | public string logo { get; private set; } 10 | 11 | public StationModel( int id, string name = "", string description = "" ) 12 | { 13 | _id = id; 14 | _name = name; 15 | _description = description; 16 | } 17 | 18 | public StationModel.fromJsonObject( Json.Object json ) 19 | { 20 | _id = (int) json.get_int_member( "id" ); 21 | _name = json.get_string_member( "name" ); 22 | _logo = json.get_string_member( "logo" ); 23 | 24 | var description = json.has_member( "genre" ) ? json.get_string_member( "genre" ) : ""; 25 | for ( int i = 2; i < 5; ++i ) 26 | { 27 | var genreMemberName = "genre%d".printf( i ); 28 | if ( json.has_member( genreMemberName ) ) 29 | { 30 | var genre = json.get_string_member( genreMemberName ); 31 | description += " / %s".printf( genre ); 32 | } 33 | } 34 | var bitrate = json.get_int_member( "br" ); 35 | var format = json.get_string_member( "mt" ); 36 | description += @" [$format, $bitrate kbps]"; 37 | _description = description; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/inotifyWatcher.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala 2 | 3 | public class Watcher : Object 4 | { 5 | private int _fd; 6 | private uint _watch; 7 | private IOChannel _channel; 8 | private uint8[] _buffer; 9 | 10 | const uint BUFFER_LENGTH = 4096; 11 | 12 | public Watcher( string path, Linux.InotifyMaskFlags mask ) 13 | { 14 | _buffer = new uint8[BUFFER_LENGTH]; 15 | 16 | _fd = Linux.inotify_init(); 17 | if ( _fd == -1 ) 18 | { 19 | error( @"Can't initialize the inotify subsystem: $(strerror(errno))" ); 20 | } 21 | 22 | _channel = new IOChannel.unix_new( _fd ); 23 | _watch = _channel.add_watch( IOCondition.IN | IOCondition.HUP, onActionFromInotify ); 24 | var ok = Linux.inotify_add_watch( _fd, path, mask ); 25 | if ( ok == -1 ) 26 | { 27 | error( @"Can't watch $path: $(strerror(errno))" ); 28 | } 29 | print( @"Watching $path...\n" ); 30 | } 31 | 32 | protected bool onActionFromInotify( IOChannel source, IOCondition condition ) 33 | { 34 | if ( ( condition & IOCondition.HUP ) == IOCondition.HUP ) 35 | { 36 | error( @"Received HUP from inotify, can't get any more updates" ); 37 | Posix.exit( -1 ); 38 | } 39 | 40 | if ( ( condition & IOCondition.IN ) == IOCondition.IN ) 41 | { 42 | assert( _fd != -1 ); 43 | assert( _buffer != null ); 44 | var bytesRead = Posix.read( _fd, _buffer, BUFFER_LENGTH ); 45 | Linux.InotifyEvent* pevent = (Linux.InotifyEvent*) _buffer; 46 | handleEvent( *pevent ); 47 | } 48 | 49 | return true; 50 | } 51 | 52 | protected void handleEvent( Linux.InotifyEvent event ) 53 | { 54 | print( "BOOM!\n" ); 55 | Posix.exit( 0 ); 56 | } 57 | 58 | ~Watcher() 59 | { 60 | if ( _watch != 0 ) 61 | { 62 | Source.remove( _watch ); 63 | } 64 | 65 | if ( _fd != -1 ) 66 | { 67 | Posix.close( _fd ); 68 | } 69 | } 70 | } 71 | 72 | int main( string[] args ) 73 | { 74 | var watcher = new Watcher( "/tmp/foo.txt", Linux.InotifyMaskFlags.ACCESS ); 75 | var loop = new MainLoop(); 76 | loop.run(); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /code/weatherClient.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala --pkg=gio-2.0 2 | 3 | void main() 4 | { 5 | var host = "api.apixu.com"; 6 | var port = 80; 7 | var key = "f4bddf887be54dc188a111908181801"; 8 | var city = "Neu-Isenburg"; 9 | var query = @"/v1/current.json?key=$key&q=$city"; 10 | var message = @"GET $query HTTP/1.1\r\nHost: $host\r\n\r\n"; 11 | 12 | try 13 | { 14 | var resolver = Resolver.get_default(); 15 | var addresses = resolver.lookup_by_name( host, null ); 16 | var address = addresses.nth_data( 0 ); 17 | stderr.printf( @"Resolved $host to $address\n" ); 18 | 19 | var client = new SocketClient(); 20 | var addr = new InetSocketAddress( address, port ); 21 | var conn = client.connect( addr ); 22 | stderr.printf( @"Connected to $host\n" ); 23 | 24 | conn.output_stream.write( message.data ); 25 | stderr.printf( @"Wrote request $message\n" ); 26 | 27 | var response = new DataInputStream( conn.input_stream ); 28 | var status_line = response.read_line( null ).strip(); 29 | stderr.printf( @"Got status line: '$status_line'\n" ); 30 | 31 | if ( ! ( "200" in status_line ) ) 32 | { 33 | error( "Service did not answer with 200 OK" ); 34 | } 35 | 36 | var headers = new HashTable( str_hash, str_equal ); 37 | var line = ""; 38 | while ( line != "\r" ) 39 | { 40 | line = response.read_line( null ); 41 | var headerComponents = line.strip().split( ":", 2 ); 42 | if ( headerComponents.length == 2 ) 43 | { 44 | var header = headerComponents[0].strip(); 45 | var value = headerComponents[1].strip(); 46 | headers[ header ] = value; 47 | stderr.printf( @"Got Header: $header = $value\n" ); 48 | } 49 | } 50 | var contentLength = headers[ "Content-Length" ].to_int(); 51 | 52 | var jsonResponse = new uint8[ contentLength ]; 53 | size_t actualLength = 0; 54 | response.read_all( jsonResponse, out actualLength ); 55 | stderr.printf( @"Got $contentLength bytes of JSON response: %s\n", jsonResponse ); 56 | stdout.printf( @"%s", jsonResponse ); 57 | 58 | } 59 | catch (Error e) 60 | { 61 | stderr.printf( @"$(e.message)\n" ); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /code/xmp/v5/libxmp.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "XMP_", lower_case_cprefix = "xmp_", cheader_filename = "xmp.h")] 2 | namespace Xmp 3 | { 4 | [CCode (cprefix = "XMP_FORMAT_")] 5 | public enum FormatFlags 6 | { 7 | 8BIT, 8 | UNSIGNED, 9 | MONO 10 | } 11 | 12 | [CCode (cname = "xmp_version")] 13 | public const string version; 14 | [CCode (cname = "xmp_vercode")] 15 | public const uint vercode; 16 | 17 | [CCode (array_null_terminated = true)] 18 | public unowned string[] get_format_list(); 19 | 20 | [CCode (cname = "struct xmp_test_info", cheader_filename = "xmp.h", destroy_function = "", has_type_id = false)] 21 | public struct TestInfo 22 | { 23 | public string name; 24 | public string type; 25 | } 26 | public static int test_module( string path, out TestInfo info ); 27 | 28 | [CCode (cname = "struct xmp_frame_info", cheader_filename = "xmp.h", destroy_function = "", has_type_id = false)] 29 | public struct FrameInfo 30 | { 31 | int pos; 32 | int pattern; 33 | int row; 34 | int num_rows; 35 | int frame; 36 | int speed; 37 | int bpm; 38 | int time; 39 | int total_time; 40 | int frame_time; 41 | [CCode (array_length = false)] 42 | uint8[] buffer; 43 | int buffer_size; 44 | int total_size; 45 | int volume; 46 | int loop_count; 47 | int virt_channels; 48 | int virt_used; 49 | int sequence; 50 | } 51 | 52 | [Compact] 53 | [CCode (cname = "void", cheader_filename = "xmp.h", free_function = "xmp_free_context")] 54 | public class Player 55 | { 56 | [CCode (cname = "xmp_create_context")] 57 | public Player(); 58 | 59 | [CCode (cname = "xmp_load_module")] 60 | public int load_module( string path ); 61 | [CCode (cname = "xmp_load_module_from_memory")] 62 | public int load_module_from_memory( uint8[] buffer ); 63 | [CCode (cname = "xmp_load_module_from_file")] 64 | public int load_module_from_file( Posix.FILE file, long size ); 65 | 66 | [CCode (cname = "xmp_start_player")] 67 | public int start( int rate = 44100, FormatFlags format = 0 ); 68 | [CCode (cname = "xmp_end_player")] 69 | public int stop(); 70 | 71 | [CCode (cname = "xmp_play_frame")] 72 | public int play_frame(); 73 | [CCode (cname = "xmp_get_frame_info")] 74 | public int get_frame_info( out FrameInfo info ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /code/netradio/dbusServer.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | const string NetRadioBusName = "de.vanille.NetRadio"; 5 | const string NetRadioPath = "/"; 6 | 7 | public class DBusServer : Object, NetRadioDBusInterface 8 | { 9 | private HashTable _knownStations; 10 | 11 | public DBusServer() 12 | { 13 | Bus.own_name( BusType.SESSION, 14 | NetRadioBusName, 15 | BusNameOwnerFlags.NONE, 16 | onBusAcquired, 17 | () => {}, 18 | () => warning( @"Could not acquire name $NetRadioBusName, the DBus interface will not be available!" ) 19 | ); 20 | 21 | 22 | _knownStations = new HashTable( str_hash, str_equal ); 23 | } 24 | 25 | // 26 | // HELPERS 27 | // 28 | 29 | void onBusAcquired( DBusConnection conn ) 30 | { 31 | try 32 | { 33 | conn.register_object( NetRadioPath, this ); 34 | } 35 | catch ( IOError e ) 36 | { 37 | error( @"Could not acquire path $NetRadioPath: $(e.message)" ); 38 | } 39 | print( @"DBus Server is now listening on $NetRadioBusName $NetRadioPath...\n" ); 40 | } 41 | 42 | // 43 | // DBUS API 44 | // 45 | public async Item[] listGenres() throws DBusError, IOError 46 | { 47 | var genres = yield DirectoryClient.sharedInstance().loadGenres(); 48 | var items = new Item[] {}; 49 | for ( int i = 0; i < genres.length; ++i ) 50 | { 51 | items += Item() { id = i, name = genres[i].title }; 52 | } 53 | return items; 54 | } 55 | 56 | public async Item[] listStations( int id ) throws NetRadioError, DBusError, IOError 57 | { 58 | var genre = new GenreModel( id ); 59 | var stations = yield DirectoryClient.sharedInstance().loadStations( genre ); 60 | var items = new Item[] {}; 61 | if ( stations == null ) 62 | { 63 | throw new NetRadioError.INVALID_GENRE( @"No stations found for genre with id $id" ); 64 | } 65 | for ( int i = 0; i < stations.length; ++i ) 66 | { 67 | var station = stations[i]; 68 | var key = @"$id.$i"; 69 | _knownStations[key] = station; 70 | items += Item() { id = i, name = station.name }; 71 | } 72 | return items; 73 | } 74 | 75 | public async void playStation( int genre, int station ) throws NetRadioError, DBusError, IOError 76 | { 77 | var key = @"$genre.$station"; 78 | var theStation = _knownStations[key]; 79 | if ( theStation != null ) 80 | { 81 | PlayerController.sharedInstance().playStation( theStation ); 82 | } 83 | else 84 | { 85 | throw new NetRadioError.INVALID_STATION( @"No station found with genre id $genre and station id $station" ); 86 | } 87 | } 88 | 89 | public async void stop() throws DBusError, IOError 90 | { 91 | PlayerController.sharedInstance().togglePlayPause(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /code/netradio/netradio-client.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | MainLoop loop; 5 | 6 | public class NetRadioClient : Object 7 | { 8 | NetRadioDBusInterface netRadioServerProxy; 9 | 10 | public bool init() 11 | { 12 | try 13 | { 14 | netRadioServerProxy = Bus.get_proxy_sync( BusType.SESSION, "de.vanille.NetRadio", "/" ); 15 | } 16 | catch ( IOError e ) 17 | { 18 | print( @"Could not create proxy: $(e.message)\n" ); 19 | return false; 20 | } 21 | 22 | return true; 23 | } 24 | 25 | public async void listGenres() 26 | { 27 | try 28 | { 29 | var items = yield netRadioServerProxy.listGenres(); 30 | foreach ( var item in items ) 31 | { 32 | print( "%03d: %s\n", item.id, item.name ); 33 | } 34 | } 35 | catch ( Error e ) 36 | { 37 | print( @"Could not list genres: $(e.message)\n" ); 38 | } 39 | loop.quit(); 40 | } 41 | 42 | public async void listStations( int genre ) 43 | { 44 | try 45 | { 46 | var items = yield netRadioServerProxy.listStations( genre ); 47 | foreach ( var item in items ) 48 | { 49 | print( "%03d: %s\n", item.id, item.name ); 50 | } 51 | } 52 | catch ( Error e ) 53 | { 54 | print( @"Could not list stations: $(e.message)\n" ); 55 | } 56 | loop.quit(); 57 | } 58 | 59 | public async void playStation( int genre, int station ) 60 | { 61 | try 62 | { 63 | yield netRadioServerProxy.playStation( genre, station ); 64 | } 65 | catch ( Error e ) 66 | { 67 | print( @"Could not play station: $(e.message)\n" ); 68 | } 69 | loop.quit(); 70 | } 71 | 72 | public async void stop() 73 | { 74 | try 75 | { 76 | yield netRadioServerProxy.stop(); 77 | } 78 | catch ( Error e ) 79 | { 80 | print( @"Could not stop: $(e.message)\n" ); 81 | } 82 | loop.quit(); 83 | } 84 | } 85 | 86 | int main( string[] args ) 87 | { 88 | var client = new NetRadioClient(); 89 | if ( !client.init() ) 90 | { 91 | return -1; 92 | } 93 | 94 | if ( args.length < 2 ) 95 | { 96 | print( "Usage: %s [param] [param]\n", args[0] ); 97 | return -1; 98 | } 99 | 100 | switch ( args[1] ) 101 | { 102 | case "lg": 103 | client.listGenres(); 104 | break; 105 | 106 | case "ls": 107 | var genre = int.parse( args[2] ); 108 | client.listStations( genre ); 109 | break; 110 | 111 | case "play": 112 | var genre = int.parse( args[2] ); 113 | var station = int.parse( args[3] ); 114 | client.playStation( genre, station ); 115 | break; 116 | 117 | case "stop": 118 | client.stop(); 119 | break; 120 | 121 | default: 122 | print( "Unknown command: %s\n", args[1] ); 123 | break; 124 | } 125 | 126 | loop = new MainLoop(); 127 | loop.run(); 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /code/ntpClient.vala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vala --pkg=Posix 2 | struct NtpPacket 3 | { 4 | uint8 li_vn_mode; // Eight bits. li, vn, and mode. 5 | // li. Two bits. Leap indicator. 6 | // vn. Three bits. Version number of the protocol. 7 | // mode. Three bits. Client will pick mode 3 for client. 8 | 9 | uint8 stratum; // Eight bits. Stratum level of the local clock. 10 | uint8 poll; // Eight bits. Maximum interval between successive messages. 11 | uint8 precision; // Eight bits. Precision of the local clock. 12 | 13 | uint32 rootDelay; // 32 bits. Total round trip delay time. 14 | uint32 rootDispersion; // 32 bits. Max error aloud from primary clock source. 15 | uint32 refId; // 32 bits. Reference clock identifier. 16 | 17 | uint32 refTm_s; // 32 bits. Reference time-stamp seconds. 18 | uint32 refTm_f; // 32 bits. Reference time-stamp fraction of a second. 19 | 20 | uint32 origTm_s; // 32 bits. Originate time-stamp seconds. 21 | uint32 origTm_f; // 32 bits. Originate time-stamp fraction of a second. 22 | 23 | uint32 rxTm_s; // 32 bits. Received time-stamp seconds. 24 | uint32 rxTm_f; // 32 bits. Received time-stamp fraction of a second. 25 | 26 | uint32 txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds. 27 | uint32 txTm_f; // 32 bits. Transmit time-stamp fraction of a second. 28 | } 29 | 30 | void main() 31 | { 32 | const string hostname = "pool.ntp.org"; 33 | const uint16 portno = 123; // NTP 34 | 35 | var packet = NtpPacket(); 36 | assert( sizeof( NtpPacket ) == 48 ); 37 | packet.li_vn_mode = 0x1b; 38 | 39 | unowned Posix.HostEnt server = Posix.gethostbyname( hostname ); // Convert URL to IP. 40 | if ( server == null ) 41 | { 42 | error( @"Can't resolve host $hostname: $(Posix.errno)" ); 43 | } 44 | print( @"Found $(server.h_addr_list.length) IP address(es) for $hostname\n" ); 45 | 46 | var address = Posix.SockAddrIn(); 47 | address.sin_family = Posix.AF_INET; 48 | address.sin_port = Posix.htons( portno ); 49 | Posix.memcpy( &address.sin_addr, server.h_addr_list[0], server.h_length ); 50 | var stringAddress = Posix.inet_ntoa( address.sin_addr ); 51 | print( @"Using $hostname IP address $stringAddress\n" ); 52 | 53 | var sockfd = Posix.socket( Posix.AF_INET, Posix.SOCK_DGRAM, Posix.IPProto.UDP ); // Create a UDP socket. 54 | if ( sockfd < 0 ) 55 | { 56 | error( @"Can't create socket: $(Posix.errno)" ); 57 | } 58 | var ok = Posix.connect( sockfd, address, sizeof( Posix.SockAddrIn ) ); 59 | if ( ok < 0 ) 60 | { 61 | error( @"Can't connect: $(Posix.errno)" ); 62 | } 63 | 64 | var written = Posix.write( sockfd, &packet, sizeof( NtpPacket ) ); 65 | if ( written < 0 ) 66 | { 67 | error( "Can't send UDP packet: $(Posix.errno)" ); 68 | } 69 | 70 | var received = Posix.read( sockfd, &packet, sizeof( NtpPacket ) ); 71 | if ( received < 0 ) 72 | { 73 | error( "Can't read from socket: $(Posix.errno)" ); 74 | } 75 | 76 | packet.txTm_s = Posix.ntohl( packet.txTm_s ); // Time-stamp seconds. 77 | packet.txTm_f = Posix.ntohl( packet.txTm_f ); // Time-stamp fraction of a second. 78 | const uint64 NTP_TIMESTAMP_DELTA = 2208988800ull; 79 | time_t txTm = ( time_t ) ( packet.txTm_s - NTP_TIMESTAMP_DELTA ); 80 | var str = Posix.ctime( ref txTm ); 81 | 82 | print( @"Current UTC time is $str" ); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /code/netradio/DirectoryClient.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | const string PrefixURL = "https://wellenreiter.vanille.de/netradio"; 5 | const string GetPrimaryCategoriesURL = PrefixURL + "/genres"; 6 | const string GetStationsForGenreURL = PrefixURL + "/genres/%u"; 7 | const string GetStationURL = PrefixURL + "/stations/%u"; 8 | 9 | public delegate void StringCompletionHandler( uint statusCode, Json.Node rootNode ); 10 | 11 | public class DirectoryClient : Object 12 | { 13 | private Soup.Session _session; 14 | private static DirectoryClient __instance; 15 | 16 | private DirectoryClient() 17 | { 18 | _session = new Soup.Session(); 19 | } 20 | 21 | // 22 | // API 23 | // 24 | public static DirectoryClient sharedInstance() 25 | { 26 | if ( __instance == null ) 27 | { 28 | __instance = new DirectoryClient(); 29 | } 30 | 31 | return __instance; 32 | } 33 | 34 | public async GenreModel[]? loadGenres() 35 | { 36 | GenreModel[] result = null; 37 | 38 | var url = GetPrimaryCategoriesURL; 39 | loadJsonForURL( url, ( statusCode, rootNode ) => { 40 | 41 | if ( statusCode == 200 ) 42 | { 43 | var genres = new GenreModel[] {}; 44 | var array = rootNode.get_array(); 45 | for ( uint i = 0; i < array.get_length(); ++i ) 46 | { 47 | var object = array.get_object_element( i ); 48 | var genre = new GenreModel.fromJsonObject( object ); 49 | genres += genre; 50 | } 51 | result = genres; 52 | } 53 | } ); 54 | 55 | return result; 56 | } 57 | 58 | public async StationModel[]? loadStations( GenreModel genre ) 59 | { 60 | StationModel[] result = null; 61 | 62 | var url = GetStationsForGenreURL.printf( genre.id ); 63 | loadJsonForURL( url, ( statusCode, rootNode ) => { 64 | 65 | if ( statusCode == 200 ) 66 | { 67 | var stations = new StationModel[] {}; 68 | var array = rootNode.get_array(); 69 | for ( uint i = 0; i < array.get_length(); ++i ) 70 | { 71 | var object = array.get_object_element( i ); 72 | var station = new StationModel.fromJsonObject( object ); 73 | stations += station; 74 | } 75 | result = stations; 76 | } 77 | 78 | } ); 79 | 80 | return result; 81 | } 82 | 83 | public async string? loadUrlForStation( StationModel station ) 84 | { 85 | string result = null; 86 | var url = GetStationURL.printf( station.id ); 87 | loadJsonForURL( url, ( statusCode, rootNode ) => { 88 | 89 | if ( statusCode == 200 ) 90 | { 91 | var object = rootNode.get_object(); 92 | result = object.get_string_member( "url" ); 93 | } 94 | 95 | } ); 96 | 97 | return result; 98 | } 99 | 100 | // 101 | // Helpers 102 | // 103 | public void loadJsonForURL( string url, StringCompletionHandler block ) 104 | { 105 | var message = new Soup.Message( "GET", url ); 106 | assert( message != null ); 107 | #if 0 // ASYNC API BROKEN 108 | _session.queue_message( message, ( session, msg ) => { 109 | print( "GET %s => %u\n%s\n", url, msg.status_code, (string) msg.response_body.data ); 110 | var rootnode = Json.from_string( (string) msg.response_body.data ); 111 | block( msg.status_code, rootnode ); 112 | } ); 113 | #else 114 | _session.send_message( message ); 115 | print( "GET %s => %u\n%s\n", url, message.status_code, (string) message.response_body.data ); 116 | var rootnode = Json.from_string( (string) message.response_body.data ); 117 | block( message.status_code, rootnode ); 118 | #endif 119 | } 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /code/netradio/directoryClient.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | const string PrefixURL = "https://wellenreiter.vanille.de/netradio"; 5 | const string GetPrimaryCategoriesURL = PrefixURL + "/genres"; 6 | const string GetStationsForGenreURL = PrefixURL + "/genres/%u"; 7 | const string GetStationURL = PrefixURL + "/stations/%u"; 8 | 9 | public delegate void StringCompletionHandler( uint statusCode, Json.Node rootNode ); 10 | 11 | public class DirectoryClient : Object 12 | { 13 | private Soup.Session _session; 14 | private static DirectoryClient __instance; 15 | 16 | private DirectoryClient() 17 | { 18 | _session = new Soup.Session(); 19 | } 20 | 21 | // 22 | // API 23 | // 24 | public static DirectoryClient sharedInstance() 25 | { 26 | if ( __instance == null ) 27 | { 28 | __instance = new DirectoryClient(); 29 | } 30 | 31 | return __instance; 32 | } 33 | 34 | public async GenreModel[]? loadGenres() 35 | { 36 | GenreModel[] result = null; 37 | 38 | var url = GetPrimaryCategoriesURL; 39 | loadJsonForURL( url, ( statusCode, rootNode ) => { 40 | 41 | if ( statusCode == 200 ) 42 | { 43 | var genres = new GenreModel[] {}; 44 | var array = rootNode.get_array(); 45 | for ( uint i = 0; i < array.get_length(); ++i ) 46 | { 47 | var object = array.get_object_element( i ); 48 | var genre = new GenreModel.fromJsonObject( object ); 49 | genres += genre; 50 | } 51 | result = genres; 52 | } 53 | } ); 54 | 55 | return result; 56 | } 57 | 58 | public async StationModel[]? loadStations( GenreModel genre ) 59 | { 60 | StationModel[] result = null; 61 | 62 | var url = GetStationsForGenreURL.printf( genre.id ); 63 | loadJsonForURL( url, ( statusCode, rootNode ) => { 64 | 65 | if ( statusCode == 200 ) 66 | { 67 | var stations = new StationModel[] {}; 68 | var array = rootNode.get_array(); 69 | for ( uint i = 0; i < array.get_length(); ++i ) 70 | { 71 | var object = array.get_object_element( i ); 72 | var station = new StationModel.fromJsonObject( object ); 73 | stations += station; 74 | } 75 | result = stations; 76 | } 77 | 78 | } ); 79 | 80 | return result; 81 | } 82 | 83 | public async string? loadUrlForStation( StationModel station ) 84 | { 85 | string result = null; 86 | var url = GetStationURL.printf( station.id ); 87 | loadJsonForURL( url, ( statusCode, rootNode ) => { 88 | 89 | if ( statusCode == 200 ) 90 | { 91 | var object = rootNode.get_object(); 92 | result = object.get_string_member( "url" ); 93 | } 94 | 95 | } ); 96 | 97 | return result; 98 | } 99 | 100 | // 101 | // Helpers 102 | // 103 | public void loadJsonForURL( string url, StringCompletionHandler block ) 104 | { 105 | var message = new Soup.Message( "GET", url ); 106 | assert( message != null ); 107 | #if 0 // ASYNC API BROKEN 108 | _session.queue_message( message, ( session, msg ) => { 109 | print( "GET %s => %u\n%s\n", url, msg.status_code, (string) msg.response_body.data ); 110 | var rootnode = Json.from_string( (string) msg.response_body.data ); 111 | block( msg.status_code, rootnode ); 112 | } ); 113 | #else 114 | _session.send_message( message ); 115 | print( "GET %s => %u\n%s\n", url, message.status_code, (string) message.response_body.data ); 116 | var rootnode = Json.from_string( (string) message.response_body.data ); 117 | block( message.status_code, rootnode ); 118 | #endif 119 | } 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /code/netradio/playerController.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | class PlayerController : Object 5 | { 6 | static PlayerController __instance; 7 | 8 | private Gst.Player _player; 9 | private Gst.PlayerState _state; 10 | 11 | public signal void didUpdateGenres( GenreModel[] genres ); 12 | public signal void didUpdateStations( StationModel[] stations ); 13 | public signal void didUpdatePlayerState(); 14 | 15 | public Gst.PlayerState state { 16 | get { 17 | return _state; 18 | } 19 | } 20 | 21 | public static PlayerController sharedInstance() 22 | { 23 | if ( __instance == null ) 24 | { 25 | __instance = new PlayerController(); 26 | } 27 | return __instance; 28 | } 29 | 30 | private PlayerController() 31 | { 32 | _player = new Gst.Player( null, null ); 33 | _player.volume = 0.5; 34 | _player.media_info_updated.connect( onPlayerMediaInfoUpdated ); 35 | _player.state_changed.connect( onPlayerStateChanged ); 36 | } 37 | 38 | // 39 | // Slots 40 | // 41 | public void onPlayerMediaInfoUpdated() 42 | { 43 | } 44 | 45 | public void onPlayerStateChanged( Gst.PlayerState state ) 46 | { 47 | _state = state; 48 | didUpdatePlayerState(); 49 | } 50 | 51 | // 52 | // API 53 | // 54 | public async void loadPrimaryCategories() 55 | { 56 | var genres = yield DirectoryClient.sharedInstance().loadGenres(); 57 | if ( genres != null ) 58 | { 59 | print( @"Received $(genres.length) genres\n" ); 60 | didUpdateGenres( genres ); 61 | } 62 | else 63 | { 64 | warning( @"Could not get genres" ); 65 | } 66 | } 67 | 68 | public async void loadStationsForGenre( GenreModel genre ) 69 | { 70 | var stations = yield DirectoryClient.sharedInstance().loadStations( genre ); 71 | if ( stations != null ) 72 | { 73 | print( @"Received $(stations.length) stations\n" ); 74 | didUpdateStations( stations ); 75 | } 76 | else 77 | { 78 | warning( @"Could not get stations" ); 79 | } 80 | } 81 | 82 | public async void playStation( StationModel station ) 83 | { 84 | var url = yield DirectoryClient.sharedInstance().loadUrlForStation( station ); 85 | _player.stop(); 86 | _player.uri = url; 87 | print( @"Player URI now $url\n" ); 88 | _player.play(); 89 | } 90 | 91 | public void togglePlayPause() 92 | { 93 | switch ( _state ) 94 | { 95 | case Gst.PlayerState.STOPPED: 96 | break; 97 | 98 | case Gst.PlayerState.BUFFERING: 99 | break; 100 | 101 | case Gst.PlayerState.PAUSED: 102 | _player.play(); 103 | break; 104 | 105 | case Gst.PlayerState.PLAYING: 106 | _player.pause(); 107 | break; 108 | 109 | default: 110 | break; 111 | } 112 | } 113 | 114 | // 115 | // Helpers 116 | // 117 | public void foo() 118 | { 119 | string infoString = ""; 120 | 121 | if ( _player != null && _player.media_info != null ) 122 | { 123 | unowned List streamList = Gst.Player.get_audio_streams( _player.media_info ); 124 | if ( streamList.length() > 0 ) 125 | { 126 | print( @"Got $(streamList.length()) streams\n" ); 127 | unowned Gst.PlayerAudioInfo stream = (Gst.PlayerAudioInfo)streamList.data; 128 | print( "%s\n", stream.get_type().name() ); 129 | 130 | 131 | print( "%p\n", streamList.data ); 132 | print( "%p\n", stream ); 133 | 134 | #if 0 135 | var title = ""; 136 | if ( stream != null ) 137 | { 138 | print( "stream: %s\n", stream.get_stream_type() ); 139 | } 140 | else 141 | { 142 | print( "stream is 0\n" ); 143 | } 144 | 145 | var taglist = stream.get_tags(); 146 | if ( taglist != null ) 147 | { 148 | print( "kurt" ); 149 | taglist.@foreach( ( list, tag ) => { 150 | print( @"got tag '$tag'\n" ); 151 | } ); 152 | } 153 | else 154 | { 155 | print( "no tag list in media_info\n" ); 156 | } 157 | #endif 158 | } 159 | } 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /code/netradio/mainWindow.vala: -------------------------------------------------------------------------------- 1 | // This file belongs to the book "Introduction to Vala Programming" – https://leanpub.com/vala 2 | // (C) 2017 Dr. Michael 'Mickey' Lauer – GPLv3 3 | 4 | class MainWindow : Gtk.ApplicationWindow 5 | { 6 | private Gtk.Box _box; 7 | private Gtk.HeaderBar _headerBar; 8 | private Gtk.Button _backButton; 9 | private Gtk.ScrolledWindow _scrolledWindow; 10 | private Gtk.TreeView _treeView; 11 | private Gtk.ActionBar _actionBar; 12 | private Gtk.Label _infoLabel; 13 | private FontAwesomeButton _playButton; 14 | private PlayerController _controller; 15 | 16 | private GenreModel[]? _genres; 17 | private GenreModel? _genre; 18 | private StationModel[]? _stations; 19 | private StationModel _station; 20 | 21 | private bool initialSelectionHack; 22 | 23 | // 24 | // Constructor 25 | // 26 | public MainWindow( Gtk.Application app, string title ) 27 | { 28 | Object( application: app, title: title ); 29 | 30 | _box = new Gtk.Box( Gtk.Orientation.VERTICAL, 0 ); 31 | _box.homogeneous = false; 32 | add( _box ); 33 | 34 | _headerBar = new Gtk.HeaderBar(); 35 | _box.pack_start( _headerBar, false, false ); 36 | 37 | _backButton = new FontAwesomeButton( 18, "" ); 38 | _backButton.clicked.connect( onBackButtonClicked ); 39 | _headerBar.pack_start( _backButton ); 40 | 41 | _scrolledWindow = new Gtk.ScrolledWindow( null, null ); 42 | _box.pack_start( _scrolledWindow, true, true ); 43 | _treeView = new Gtk.TreeView(); 44 | _scrolledWindow.add( _treeView ); 45 | 46 | _treeView.set_grid_lines( Gtk.TreeViewGridLines.HORIZONTAL ); 47 | _treeView.activate_on_single_click = true; 48 | _treeView.get_selection().changed.connect( onTreeViewSelectionChanged ); 49 | var cell = new Gtk.CellRendererText(); 50 | cell.set_padding( 4, 10 ); 51 | _treeView.insert_column_with_attributes( -1, "Name & Description", cell, "markup", 0 ); 52 | 53 | _actionBar = new Gtk.ActionBar(); 54 | _box.pack_start( _actionBar, false, false ); 55 | 56 | _infoLabel = new Gtk.Label( "" ); 57 | _actionBar.set_center_widget( _infoLabel ); 58 | _playButton = new FontAwesomeButton( 18, "" ); 59 | _playButton.clicked.connect( onPlayButtonClicked ); 60 | _actionBar.pack_end( _playButton ); 61 | 62 | _controller = PlayerController.sharedInstance(); 63 | _controller.didUpdateGenres.connect( onControllerDidUpdateGenres ); 64 | _controller.didUpdateStations.connect( onControllerDidUpdateStations ); 65 | _controller.didUpdatePlayerState.connect( onControllerDidUpdatePlayerState ); 66 | 67 | _controller.loadPrimaryCategories(); 68 | //_controller.playStation( new StationModel() ); 69 | } 70 | 71 | // 72 | // Helpers 73 | // 74 | private void updateUI() 75 | { 76 | updateHeader(); 77 | updateList(); 78 | updateActionBar(); 79 | } 80 | 81 | private void updateHeader() 82 | { 83 | if ( _genre != null ) 84 | { 85 | _headerBar.title = _genre.title; 86 | _headerBar.subtitle = _genre.description; 87 | _backButton.sensitive = true; 88 | } 89 | else 90 | { 91 | _headerBar.title = "All Genres"; 92 | _headerBar.subtitle = null; 93 | _backButton.sensitive = false; 94 | } 95 | } 96 | 97 | private void updateList() 98 | { 99 | var listmodel = new Gtk.ListStore( 1, typeof(string) ); 100 | _treeView.set_model( listmodel ); 101 | 102 | Gtk.TreeIter iter; 103 | 104 | if ( _stations == null ) 105 | { 106 | for( int i = 0; i < _genres.length; ++i ) 107 | { 108 | listmodel.append( out iter ); 109 | var title = markupSanitize( _genres[i].title ); 110 | var subtitle = markupSanitize( _genres[i].description ); 111 | if ( subtitle.length == 0 ) 112 | { 113 | subtitle = title; 114 | } 115 | var str = "%s\n\n%s".printf( title, subtitle ); 116 | listmodel.set( iter, 0, str ); 117 | } 118 | } 119 | else 120 | { 121 | for( int i = 0; i < _stations.length; ++i ) 122 | { 123 | listmodel.append( out iter ); 124 | var title = markupSanitize( _stations[i].name ); 125 | var subtitle = markupSanitize( _stations[i].description ); 126 | var str = "%s\n\n%s".printf( title, subtitle ); 127 | listmodel.set( iter, 0, str ); 128 | } 129 | } 130 | } 131 | 132 | private void updateActionBar() 133 | { 134 | string stateString = ""; 135 | 136 | switch ( _controller.state ) 137 | { 138 | case Gst.PlayerState.STOPPED: 139 | stateString = "STOPPED"; 140 | _playButton.icon = ""; 141 | _playButton.set_sensitive( false ); 142 | break; 143 | 144 | case Gst.PlayerState.BUFFERING: 145 | stateString = "BUFFERING..."; 146 | _playButton.icon = ""; 147 | _playButton.set_sensitive( false ); 148 | break; 149 | 150 | case Gst.PlayerState.PAUSED: 151 | stateString = "PAUSED"; 152 | _playButton.icon = ""; 153 | _playButton.set_sensitive( true ); 154 | break; 155 | 156 | case Gst.PlayerState.PLAYING: 157 | stateString = "PLAYING"; 158 | _playButton.icon = ""; 159 | _playButton.set_sensitive( true ); 160 | break; 161 | } 162 | 163 | var str = @"$stateString"; 164 | _infoLabel.set_markup( str ); 165 | } 166 | 167 | private string markupSanitize( string text ) 168 | { 169 | return text; //text.replace( "&", "&" ); 170 | } 171 | 172 | // 173 | // Slots 174 | // 175 | 176 | private void onControllerDidUpdateGenres( GenreModel[] genres ) 177 | { 178 | _genres = genres; 179 | _stations = null; 180 | updateUI(); 181 | } 182 | 183 | private void onControllerDidUpdateStations( StationModel[] stations ) 184 | { 185 | _stations = stations; 186 | updateUI(); 187 | } 188 | 189 | private void onControllerDidUpdatePlayerState() 190 | { 191 | updateActionBar(); 192 | } 193 | 194 | private void onTreeViewSelectionChanged() 195 | { 196 | Gtk.TreeModel model; 197 | var paths = _treeView.get_selection().get_selected_rows( out model ); 198 | if ( paths.length() == 0 ) 199 | { 200 | return; 201 | } 202 | if ( !initialSelectionHack ) 203 | { 204 | _treeView.get_selection().unselect_all(); 205 | initialSelectionHack = true; 206 | print( "selection hack done\n" ); 207 | return; 208 | } 209 | Gtk.TreePath path = paths.first().data; 210 | int[] indices = path.get_indices(); 211 | var rowIndex = indices[0]; 212 | 213 | if ( _genre == null ) 214 | { 215 | _genre = _genres[rowIndex]; 216 | _controller.loadStationsForGenre( _genre ); 217 | } 218 | else // stations are in 219 | { 220 | _station = _stations[rowIndex]; 221 | _controller.playStation( _station ); 222 | } 223 | } 224 | 225 | private void onBackButtonClicked() 226 | { 227 | _genre = null; 228 | _stations = null; 229 | updateUI(); 230 | } 231 | 232 | private void onPlayButtonClicked() 233 | { 234 | _controller.togglePlayPause(); 235 | } 236 | 237 | private void onPlayerStateChanged() 238 | { 239 | updateActionBar(); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /code/gusb/gusb.vapi: -------------------------------------------------------------------------------- 1 | /* gusb.vapi generated by vapigen, do not modify. */ 2 | 3 | [CCode (cprefix = "GUsb", gir_namespace = "GUsb", gir_version = "1.0", lower_case_cprefix = "g_usb_")] 4 | namespace GUsb { 5 | [CCode (cheader_filename = "gusb.h", type_id = "g_usb_context_get_type ()")] 6 | public class Context : GLib.Object, GLib.Initable { 7 | [CCode (has_construct_function = false)] 8 | [Version (since = "0.1.0")] 9 | public Context () throws GLib.Error; 10 | [Version (since = "0.2.2")] 11 | public void enumerate (); 12 | [Version (since = "0.1.0")] 13 | public static GLib.Quark error_quark (); 14 | [Version (since = "0.2.2")] 15 | public GUsb.Device find_by_bus_address (uint8 bus, uint8 address) throws GLib.Error; 16 | [Version (since = "0.2.4")] 17 | public GUsb.Device find_by_platform_id (string platform_id) throws GLib.Error; 18 | [Version (since = "0.2.2")] 19 | public GUsb.Device find_by_vid_pid (uint16 vid, uint16 pid) throws GLib.Error; 20 | [Version (since = "0.2.2")] 21 | public GLib.GenericArray get_devices (); 22 | [Version (since = "0.2.11")] 23 | public GUsb.ContextFlags get_flags (); 24 | [Version (since = "0.2.5")] 25 | public unowned GLib.MainContext get_main_context (); 26 | [Version (since = "0.1.0")] 27 | public unowned GUsb.Source get_source (GLib.MainContext main_ctx); 28 | [Version (since = "0.1.0")] 29 | public void set_debug (GLib.LogLevelFlags flags); 30 | [Version (since = "0.2.11")] 31 | public void set_flags (GUsb.ContextFlags flags); 32 | [Version (since = "0.2.5")] 33 | public void set_main_context (GLib.MainContext main_ctx); 34 | [Version (since = "0.2.9")] 35 | public GUsb.Device wait_for_replug (GUsb.Device device, uint timeout_ms) throws GLib.Error; 36 | [NoAccessorMethod] 37 | public int debug_level { get; set; } 38 | [NoAccessorMethod] 39 | public void* libusb_context { get; } 40 | public virtual signal void device_added (GUsb.Device device); 41 | public virtual signal void device_removed (GUsb.Device device); 42 | } 43 | [CCode (cheader_filename = "gusb.h", type_id = "g_usb_device_get_type ()")] 44 | public class Device : GLib.Object, GLib.Initable { 45 | [CCode (has_construct_function = false)] 46 | protected Device (); 47 | [Version (since = "0.1.0")] 48 | public bool bulk_transfer (uint8 endpoint, [CCode (array_length_cname = "length", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, size_t actual_length, uint timeout, GLib.Cancellable? cancellable = null) throws GLib.Error; 49 | [Version (since = "0.1.0")] 50 | public async ssize_t bulk_transfer_async (uint8 endpoint, [CCode (array_length_cname = "length", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, uint timeout, GLib.Cancellable? cancellable) throws GLib.Error; 51 | [Version (since = "0.1.0")] 52 | public bool claim_interface (int @interface, GUsb.DeviceClaimInterfaceFlags flags) throws GLib.Error; 53 | public bool close () throws GLib.Error; 54 | [Version (since = "0.1.0")] 55 | public bool control_transfer (GUsb.DeviceDirection direction, GUsb.DeviceRequestType request_type, GUsb.DeviceRecipient recipient, uint8 request, uint16 value, uint16 idx, [CCode (array_length_cname = "length", array_length_pos = 7.5, array_length_type = "gsize")] uint8[] data, size_t actual_length, uint timeout, GLib.Cancellable? cancellable = null) throws GLib.Error; 56 | [Version (since = "0.1.0")] 57 | public async ssize_t control_transfer_async (GUsb.DeviceDirection direction, GUsb.DeviceRequestType request_type, GUsb.DeviceRecipient recipient, uint8 request, uint16 value, uint16 idx, [CCode (array_length_cname = "length", array_length_pos = 7.5, array_length_type = "gsize")] uint8[] data, uint timeout, GLib.Cancellable? cancellable) throws GLib.Error; 58 | [Version (since = "0.1.0")] 59 | public static GLib.Quark error_quark (); 60 | [Version (since = "0.1.0")] 61 | public uint8 get_address (); 62 | [Version (since = "0.1.0")] 63 | public uint8 get_bus (); 64 | [Version (since = "0.2.4")] 65 | public GLib.GenericArray get_children (); 66 | [Version (since = "0.1.0")] 67 | public int get_configuration () throws GLib.Error; 68 | [Version (since = "0.2.5")] 69 | public uint8 get_custom_index (uint8 class_id, uint8 subclass_id, uint8 protocol_id) throws GLib.Error; 70 | [Version (since = "0.1.7")] 71 | public uint8 get_device_class (); 72 | [Version (since = "0.2.4")] 73 | public uint8 get_device_protocol (); 74 | [Version (since = "0.2.4")] 75 | public uint8 get_device_subclass (); 76 | [Version (since = "0.2.8")] 77 | public GUsb.Interface get_interface (uint8 class_id, uint8 subclass_id, uint8 protocol_id) throws GLib.Error; 78 | [Version (since = "0.2.8")] 79 | public GLib.GenericArray get_interfaces () throws GLib.Error; 80 | [Version (since = "0.1.0")] 81 | public uint8 get_manufacturer_index (); 82 | [Version (since = "0.2.4")] 83 | public GUsb.Device get_parent (); 84 | [Version (since = "0.1.0")] 85 | public uint16 get_pid (); 86 | [Version (since = "0.2.4")] 87 | public unowned string get_pid_as_str (); 88 | [Version (since = "0.1.1")] 89 | public unowned string get_platform_id (); 90 | [Version (since = "0.2.4")] 91 | public uint8 get_port_number (); 92 | [Version (since = "0.1.0")] 93 | public uint8 get_product_index (); 94 | [Version (since = "0.2.8")] 95 | public uint16 get_release (); 96 | [Version (since = "0.1.0")] 97 | public uint8 get_serial_number_index (); 98 | [Version (since = "0.1.0")] 99 | public string get_string_descriptor (uint8 desc_index) throws GLib.Error; 100 | [Version (since = "0.1.0")] 101 | public uint16 get_vid (); 102 | [Version (since = "0.2.4")] 103 | public unowned string get_vid_as_str (); 104 | [Version (since = "0.1.0")] 105 | public bool interrupt_transfer (uint8 endpoint, [CCode (array_length_cname = "length", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, size_t actual_length, uint timeout, GLib.Cancellable? cancellable = null) throws GLib.Error; 106 | [Version (since = "0.1.0")] 107 | public async ssize_t interrupt_transfer_async (uint8 endpoint, [CCode (array_length_cname = "length", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, uint timeout, GLib.Cancellable? cancellable) throws GLib.Error; 108 | [Version (since = "0.1.0")] 109 | public bool open () throws GLib.Error; 110 | [Version (since = "0.1.0")] 111 | public bool release_interface (int @interface, GUsb.DeviceClaimInterfaceFlags flags) throws GLib.Error; 112 | public bool reset () throws GLib.Error; 113 | [Version (since = "0.1.0")] 114 | public bool set_configuration (int configuration) throws GLib.Error; 115 | [Version (since = "0.2.8")] 116 | public bool set_interface_alt (int @interface, uint8 alt) throws GLib.Error; 117 | public GUsb.Context context { construct; } 118 | [NoAccessorMethod] 119 | public void* libusb_device { get; construct; } 120 | public string platform_id { construct; } 121 | } 122 | [CCode (cheader_filename = "gusb.h", type_id = "g_usb_device_list_get_type ()")] 123 | public class DeviceList : GLib.Object { 124 | [CCode (has_construct_function = false)] 125 | [Version (since = "0.1.0")] 126 | public DeviceList (GUsb.Context context); 127 | [Version (since = "0.1.0")] 128 | public void coldplug (); 129 | [Version (since = "0.1.0")] 130 | public GUsb.Device find_by_bus_address (uint8 bus, uint8 address) throws GLib.Error; 131 | [Version (since = "0.1.0")] 132 | public GUsb.Device find_by_vid_pid (uint16 vid, uint16 pid) throws GLib.Error; 133 | [Version (since = "0.1.0")] 134 | public GLib.GenericArray get_devices (); 135 | [NoAccessorMethod] 136 | public GUsb.Context context { owned get; construct; } 137 | public virtual signal void device_added (GUsb.Device device); 138 | public virtual signal void device_removed (GUsb.Device device); 139 | } 140 | [CCode (cheader_filename = "gusb.h", type_id = "g_usb_interface_get_type ()")] 141 | public class Interface : GLib.Object { 142 | [CCode (has_construct_function = false)] 143 | protected Interface (); 144 | [Version (since = "0.2.8")] 145 | public uint8 get_alternate (); 146 | [Version (since = "0.2.8")] 147 | public uint8 get_class (); 148 | [Version (since = "0.2.8")] 149 | public unowned GLib.Bytes get_extra (); 150 | [Version (since = "0.2.8")] 151 | public uint8 get_index (); 152 | [Version (since = "0.2.8")] 153 | public uint8 get_kind (); 154 | [Version (since = "0.2.8")] 155 | public uint8 get_length (); 156 | [Version (since = "0.2.8")] 157 | public uint8 get_number (); 158 | [Version (since = "0.2.8")] 159 | public uint8 get_protocol (); 160 | [Version (since = "0.2.8")] 161 | public uint8 get_subclass (); 162 | } 163 | [CCode (cheader_filename = "gusb.h", has_type_id = false)] 164 | [Compact] 165 | public class Source { 166 | [Version (since = "0.1.0")] 167 | public void set_callback (owned GLib.SourceFunc func); 168 | } 169 | [CCode (cheader_filename = "gusb.h")] 170 | [SimpleType] 171 | public struct Context_autoptr { 172 | } 173 | [CCode (cheader_filename = "gusb.h")] 174 | [SimpleType] 175 | public struct DeviceList_autoptr { 176 | } 177 | [CCode (cheader_filename = "gusb.h")] 178 | [SimpleType] 179 | public struct Device_autoptr { 180 | } 181 | [CCode (cheader_filename = "gusb.h")] 182 | [SimpleType] 183 | public struct Interface_autoptr { 184 | } 185 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_CONTEXT_ERROR_", has_type_id = false)] 186 | public enum ContextError { 187 | [CCode (cname = "G_USB_CONTEXT_ERROR_INTERNAL")] 188 | CONTEXT_ERROR_INTERNAL 189 | } 190 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_CONTEXT_FLAGS_", has_type_id = false)] 191 | [Flags] 192 | public enum ContextFlags { 193 | NONE, 194 | AUTO_OPEN_DEVICES 195 | } 196 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_", has_type_id = false)] 197 | [Flags] 198 | public enum DeviceClaimInterfaceFlags { 199 | [CCode (cname = "G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER")] 200 | DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER 201 | } 202 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_CLASS_", has_type_id = false)] 203 | public enum DeviceClassCode { 204 | INTERFACE_DESC, 205 | AUDIO, 206 | COMMUNICATIONS, 207 | HID, 208 | PHYSICAL, 209 | IMAGE, 210 | PRINTER, 211 | MASS_STORAGE, 212 | HUB, 213 | CDC_DATA, 214 | SMART_CARD, 215 | CONTENT_SECURITY, 216 | VIDEO, 217 | PERSONAL_HEALTHCARE, 218 | AUDIO_VIDEO, 219 | BILLBOARD, 220 | DIAGNOSTIC, 221 | WIRELESS_CONTROLLER, 222 | MISCELLANEOUS, 223 | APPLICATION_SPECIFIC, 224 | VENDOR_SPECIFIC 225 | } 226 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_DIRECTION_", has_type_id = false)] 227 | public enum DeviceDirection { 228 | DEVICE_TO_HOST, 229 | HOST_TO_DEVICE 230 | } 231 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_ERROR_", has_type_id = false)] 232 | public enum DeviceError { 233 | INTERNAL, 234 | IO, 235 | TIMED_OUT, 236 | NOT_SUPPORTED, 237 | NO_DEVICE, 238 | NOT_OPEN, 239 | ALREADY_OPEN, 240 | CANCELLED, 241 | FAILED, 242 | PERMISSION_DENIED, 243 | LAST 244 | } 245 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_RECIPIENT_", has_type_id = false)] 246 | public enum DeviceRecipient { 247 | DEVICE, 248 | INTERFACE, 249 | ENDPOINT, 250 | OTHER 251 | } 252 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_DEVICE_REQUEST_TYPE_", has_type_id = false)] 253 | public enum DeviceRequestType { 254 | STANDARD, 255 | CLASS, 256 | VENDOR, 257 | RESERVED 258 | } 259 | [CCode (cheader_filename = "gusb.h", cprefix = "G_USB_SOURCE_ERROR_")] 260 | public errordomain SourceError { 261 | [CCode (cname = "G_USB_SOURCE_ERROR_INTERNAL")] 262 | SOURCE_ERROR_INTERNAL; 263 | [Version (since = "0.1.0")] 264 | public static GLib.Quark quark (); 265 | } 266 | [CCode (cheader_filename = "gusb.h", cname = "G_USB_MAJOR_VERSION")] 267 | public const int MAJOR_VERSION; 268 | [CCode (cheader_filename = "gusb.h", cname = "G_USB_MICRO_VERSION")] 269 | public const int MICRO_VERSION; 270 | [CCode (cheader_filename = "gusb.h", cname = "G_USB_MINOR_VERSION")] 271 | public const int MINOR_VERSION; 272 | [CCode (cheader_filename = "gusb.h")] 273 | public static unowned string strerror (int error_code); 274 | } 275 | --------------------------------------------------------------------------------