[ffigen] Added support for c99 bool (#86)

* Bool datatype is now mapped to Uint8, Added test in native_test

* Added dart-bool to config, bools in function parameters and return type now use dart bool instead of int by default
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 6dfc92d..38fcaa2 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.4
+- Added support for C booleans as Uint8.
+- Added config `dart-bool` (default: true) to use dart bool instead of int in function parameters and return type.
+
 # 0.2.3+3
 - Wrapper dynamic library version now uses ffigen version from its pubspec.yaml file.
 
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md
index 6896b87..7fa1a1b 100644
--- a/pkgs/ffigen/README.md
+++ b/pkgs/ffigen/README.md
@@ -175,6 +175,13 @@
     <td><pre lang="yaml">use-supported-typedefs: true</pre></td>
   </tr>
   <tr>
+    <td>dart-bool</td>
+    <td>Should generate dart `bool` for c99 bool in functions.<br>
+    <b>Default: true</b>
+    </td>
+    <td><pre lang="yaml">dart-bool: true</pre></td>
+  </tr>
+  <tr>
     <td>unnamed-enums</td>
     <td>Should generate constants for anonymous unnamed enums.<br>
     <b>Default: true</b>
@@ -355,3 +362,8 @@
 
 Ffigen treats `char*` just as any other pointer,(`Pointer<Int8>`).
 To convert these to/from `String`, you can use [package:ffi](https://pub.dev/packages/ffi) and use `Utf8.fromUtf8(ptr.cast())` to convert `char*` to dart `string`.
+### How does ffigen handle C99 bool data type?
+
+Although `dart:ffi` doesn't have a NativeType for `bool`, they can be implemented as `Uint8`.
+Ffigen generates dart `bool` for function parameters and return type by default.
+To disable this, and use `int` instead, set `dart-bool: false` in configurations.
diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart
index c1f444b..6498ff7 100644
--- a/pkgs/ffigen/lib/src/code_generator/func.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func.dart
@@ -114,9 +114,19 @@
       p.name = paramNamer.makeUnique(p.name);
     }
     // Write enclosing function.
-    s.write('${returnType.getDartType(w)} $enclosingFuncName(\n');
+    if (w.dartBool && returnType.broadType == BroadType.Boolean) {
+      // Use bool return type in enclosing function.
+      s.write('bool $enclosingFuncName(\n');
+    } else {
+      s.write('${returnType.getDartType(w)} $enclosingFuncName(\n');
+    }
     for (final p in parameters) {
-      s.write('  ${p.type.getDartType(w)} ${p.name},\n');
+      if (w.dartBool && p.type.broadType == BroadType.Boolean) {
+        // Use bool parameter type in enclosing function.
+        s.write('  bool ${p.name},\n');
+      } else {
+        s.write('  ${p.type.getDartType(w)} ${p.name},\n');
+      }
     }
     s.write(') {\n');
     s.write(
@@ -124,9 +134,19 @@
 
     s.write('  return $funcVarName(\n');
     for (final p in parameters) {
-      s.write('    ${p.name},\n');
+      if (w.dartBool && p.type.broadType == BroadType.Boolean) {
+        // Convert bool parameter to int before calling.
+        s.write('    ${p.name}?1:0,\n');
+      } else {
+        s.write('    ${p.name},\n');
+      }
     }
-    s.write('  );\n');
+    if (w.dartBool && returnType.broadType == BroadType.Boolean) {
+      // Convert int return type to bool.
+      s.write('  )!=0;\n');
+    } else {
+      s.write('  );\n');
+    }
     s.write('}\n');
 
     // Write function variable.
diff --git a/pkgs/ffigen/lib/src/code_generator/library.dart b/pkgs/ffigen/lib/src/code_generator/library.dart
index 2daa009..978105e 100644
--- a/pkgs/ffigen/lib/src/code_generator/library.dart
+++ b/pkgs/ffigen/lib/src/code_generator/library.dart
@@ -27,6 +27,7 @@
     String description,
     @required this.bindings,
     String header,
+    bool dartBool = true,
   }) {
     // Seperate bindings which require lookup.
     final lookUpBindings = bindings.whereType<LookUpBinding>().toList();
@@ -35,15 +36,15 @@
     /// Handle any declaration-declaration name conflict in [lookUpBindings].
     final lookUpDeclConflictHandler = UniqueNamer({});
     for (final b in lookUpBindings) {
-      _warnPrivateDeclaration(b);
-      _resolveNameConflict(lookUpDeclConflictHandler, b);
+      _warnIfPrivateDeclaration(b);
+      _resolveIfNameConflicts(lookUpDeclConflictHandler, b);
     }
 
     /// Handle any declaration-declaration name conflict in [noLookUpBindings].
     final noLookUpDeclConflictHandler = UniqueNamer({});
     for (final b in noLookUpBindings) {
-      _warnPrivateDeclaration(b);
-      _resolveNameConflict(noLookUpDeclConflictHandler, b);
+      _warnIfPrivateDeclaration(b);
+      _resolveIfNameConflicts(noLookUpDeclConflictHandler, b);
     }
 
     _writer = Writer(
@@ -52,19 +53,20 @@
       className: name,
       classDocComment: description,
       header: header,
+      dartBool: dartBool,
     );
   }
 
   /// Logs a warning if generated declaration will be private.
-  void _warnPrivateDeclaration(Binding b) {
+  void _warnIfPrivateDeclaration(Binding b) {
     if (b.name.startsWith('_')) {
       _logger.warning(
           "Generated declaration '${b.name}' start's with '_' and therefore will be private.");
     }
   }
 
-  /// LResolves name conflict(if any) and logs a warning.
-  void _resolveNameConflict(UniqueNamer namer, Binding b) {
+  /// Resolves name conflict(if any) and logs a warning.
+  void _resolveIfNameConflicts(UniqueNamer namer, Binding b) {
     // Print warning if name was conflicting and has been changed.
     if (namer.isUsed(b.name)) {
       final oldName = b.name;
diff --git a/pkgs/ffigen/lib/src/code_generator/type.dart b/pkgs/ffigen/lib/src/code_generator/type.dart
index 694bacf..9ccc2b1 100644
--- a/pkgs/ffigen/lib/src/code_generator/type.dart
+++ b/pkgs/ffigen/lib/src/code_generator/type.dart
@@ -33,6 +33,7 @@
 
 /// The basic types in which all types can be broadly classified into.
 enum BroadType {
+  Boolean,
   NativeType,
   Pointer,
   Struct,
@@ -120,6 +121,11 @@
       child: elementType,
     );
   }
+  factory Type.boolean() {
+    return Type._(
+      broadType: BroadType.Boolean,
+    );
+  }
   factory Type.unimplemented(String reason) {
     return Type._(
         broadType: BroadType.Unimplemented, unimplementedReason: reason);
@@ -149,7 +155,8 @@
     }
   }
 
-  bool get isPrimitive => broadType == BroadType.NativeType;
+  bool get isPrimitive =>
+      (broadType == BroadType.NativeType || broadType == BroadType.Boolean);
 
   String getCType(Writer w) {
     switch (broadType) {
@@ -167,6 +174,8 @@
       case BroadType
           .ConstantArray: // Array parameters are treated as Pointers in C.
         return '${w.ffiLibraryPrefix}.Pointer<${child.getCType(w)}>';
+      case BroadType.Boolean: // Booleans are treated as uint8.
+        return '${w.ffiLibraryPrefix}.${_primitives[SupportedNativeType.Uint8].c}';
       default:
         throw Exception('cType unknown');
     }
@@ -188,6 +197,8 @@
       case BroadType
           .ConstantArray: // Array parameters are treated as Pointers in C.
         return '${w.ffiLibraryPrefix}.Pointer<${child.getCType(w)}>';
+      case BroadType.Boolean: // Booleans are treated as uint8.
+        return _primitives[SupportedNativeType.Uint8].dart;
       default:
         throw Exception('dart type unknown for ${broadType.toString()}');
     }
diff --git a/pkgs/ffigen/lib/src/code_generator/writer.dart b/pkgs/ffigen/lib/src/code_generator/writer.dart
index 41be178..0a02ed0 100644
--- a/pkgs/ffigen/lib/src/code_generator/writer.dart
+++ b/pkgs/ffigen/lib/src/code_generator/writer.dart
@@ -27,6 +27,8 @@
   String _dylibIdentifier;
   String get dylibIdentifier => _dylibIdentifier;
 
+  final bool dartBool;
+
   /// Initial namers set after running constructor. Namers are reset to this
   /// initial state everytime [generate] is called.
   UniqueNamer _initialTopLevelUniqueNamer, _initialWrapperLevelUniqueNamer;
@@ -47,9 +49,11 @@
     @required this.lookUpBindings,
     @required this.noLookUpBindings,
     @required String className,
+    @required this.dartBool,
     this.classDocComment,
     this.header,
-  }) : assert(className != null) {
+  })  : assert(className != null),
+        assert(dartBool != null) {
     final globalLevelNameSet = noLookUpBindings.map((e) => e.name).toSet();
     final wrapperLevelNameSet = lookUpBindings.map((e) => e.name).toSet();
     final allNameSet = <String>{}
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart
index 2b9df49..55ff570 100644
--- a/pkgs/ffigen/lib/src/config_provider/config.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -70,6 +70,10 @@
   bool get unnamedEnums => _unnamedEnums;
   bool _unnamedEnums;
 
+  /// If dart bool should be generated for C booleans.
+  bool get dartBool => _dartBool;
+  bool _dartBool;
+
   /// Name of the wrapper class.
   String get wrapperName => _wrapperName;
   String _wrapperName;
@@ -248,6 +252,13 @@
         defaultValue: () => true,
         extractedResult: (dynamic result) => _unnamedEnums = result as bool,
       ),
+      strings.dartBool: Specification<bool>(
+        requirement: Requirement.no,
+        validator: booleanValidator,
+        extractor: booleanExtractor,
+        defaultValue: () => true,
+        extractedResult: (dynamic result) => _dartBool = result as bool,
+      ),
       strings.name: Specification<String>(
         requirement: Requirement.prefer,
         validator: dartClassNameValidator,
diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart
index 4036a4c..0cbe3c8 100644
--- a/pkgs/ffigen/lib/src/header_parser/parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/parser.dart
@@ -30,6 +30,7 @@
     name: config.wrapperName,
     description: config.wrapperDocComment,
     header: config.preamble,
+    dartBool: config.dartBool,
   );
 
   if (config.sort) {
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
index 0e6ec0f..3e74ea6 100644
--- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
+++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -73,6 +73,8 @@
       return Type.incompleteArray(
         clang.clang_getArrayElementType_wrap(cxtype).toCodeGenTypeAndDispose(),
       );
+    case clang_types.CXTypeKind.CXType_Bool:
+      return Type.boolean();
     default:
       if (cxTypeKindToSupportedNativeTypes.containsKey(kind)) {
         return Type.nativeType(
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index 53bd07f..0749dbe 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -83,6 +83,7 @@
 const warnWhenRemoving = 'warn-when-removing';
 const arrayWorkaround = 'array-workaround';
 const unnamedEnums = 'unnamed-enums';
+const dartBool = 'dart-bool';
 
 const comments = 'comments';
 // Sub-fields of comments
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index 57443c8..0d52538 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,9 +3,9 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 0.2.3+3
+version: 0.2.4
 homepage: https://github.com/dart-lang/ffigen
-description: Experimental generator for FFI bindings, using LibClang to parse C/C++ header files.
+description: Experimental generator for FFI bindings, using LibClang to parse C header files.
 
 environment:
   sdk: '>=2.7.0 <3.0.0'
diff --git a/pkgs/ffigen/test/code_generator_test.dart b/pkgs/ffigen/test/code_generator_test.dart
index 7abf5df..05dceb1 100644
--- a/pkgs/ffigen/test/code_generator_test.dart
+++ b/pkgs/ffigen/test/code_generator_test.dart
@@ -758,4 +758,166 @@
       }
     });
   });
+  test('boolean_dartBool', () {
+    final library = Library(
+      name: 'Bindings',
+      dartBool: true,
+      bindings: [
+        Func(
+          name: 'test1',
+          returnType: Type.boolean(),
+          parameters: [
+            Parameter(name: 'a', type: Type.boolean()),
+            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+          ],
+        ),
+        Struc(
+          name: 'test2',
+          members: [
+            Member(name: 'a', type: Type.boolean()),
+          ],
+        ),
+      ],
+    );
+
+    final gen = library.generate();
+
+    // Writing to file for debug purpose.
+    final file = File(
+      'test/debug_generated/boolean-dartbool-output.dart',
+    );
+    try {
+      expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
+// 
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings{
+/// Holds the Dynamic library.
+final ffi.DynamicLibrary _dylib;
+
+/// The symbols are looked up in [dynamicLibrary].
+Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
+
+bool test1(
+  bool a,
+  ffi.Pointer<ffi.Uint8> b,
+) {
+_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1');
+  return _test1(
+    a?1:0,
+    b,
+  )!=0;
+}
+_dart_test1 _test1;
+
+}
+
+class test2 extends ffi.Struct{
+  @ffi.Uint8()
+  int a;
+
+}
+
+typedef _c_test1 = ffi.Uint8 Function(
+  ffi.Uint8 a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+typedef _dart_test1 = int Function(
+  int a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+''');
+      if (file.existsSync()) {
+        file.delete();
+      }
+    } catch (e) {
+      file.writeAsStringSync(gen);
+      print('Failed test, Debug output: ${file.absolute?.path}');
+      rethrow;
+    }
+  });
+  test('boolean_no_dartBool', () {
+    final library = Library(
+      name: 'Bindings',
+      dartBool: false,
+      bindings: [
+        Func(
+          name: 'test1',
+          returnType: Type.boolean(),
+          parameters: [
+            Parameter(name: 'a', type: Type.boolean()),
+            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+          ],
+        ),
+        Struc(
+          name: 'test2',
+          members: [
+            Member(name: 'a', type: Type.boolean()),
+          ],
+        ),
+      ],
+    );
+
+    final gen = library.generate();
+
+    // Writing to file for debug purpose.
+    final file = File(
+      'test/debug_generated/boolean-no-dartBool-output.dart',
+    );
+    try {
+      expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
+// 
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings{
+/// Holds the Dynamic library.
+final ffi.DynamicLibrary _dylib;
+
+/// The symbols are looked up in [dynamicLibrary].
+Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
+
+int test1(
+  int a,
+  ffi.Pointer<ffi.Uint8> b,
+) {
+_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1');
+  return _test1(
+    a,
+    b,
+  );
+}
+_dart_test1 _test1;
+
+}
+
+class test2 extends ffi.Struct{
+  @ffi.Uint8()
+  int a;
+
+}
+
+typedef _c_test1 = ffi.Uint8 Function(
+  ffi.Uint8 a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+typedef _dart_test1 = int Function(
+  int a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+''');
+      if (file.existsSync()) {
+        file.delete();
+      }
+    } catch (e) {
+      file.writeAsStringSync(gen);
+      print('Failed test, Debug output: ${file.absolute?.path}');
+      rethrow;
+    }
+  });
 }
diff --git a/pkgs/ffigen/test/native_test/config.yaml b/pkgs/ffigen/test/native_test/config.yaml
index a81d932..55b0b0b 100644
--- a/pkgs/ffigen/test/native_test/config.yaml
+++ b/pkgs/ffigen/test/native_test/config.yaml
@@ -16,3 +16,6 @@
   include-directives:
     - '**native_test.c'
 array-workaround: true
+
+# Needed for stdbool.h in MacOS
+compiler-opts: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Headers/'
diff --git a/pkgs/ffigen/test/native_test/native_test.c b/pkgs/ffigen/test/native_test/native_test.c
index 21c2f16..57b23c0 100644
--- a/pkgs/ffigen/test/native_test/native_test.c
+++ b/pkgs/ffigen/test/native_test/native_test.c
@@ -5,6 +5,9 @@
 
 #include <stdint.h>
 #include <stdlib.h>
+#include <stdbool.h>
+
+bool Function1Bool(bool x) { return !x; }
 
 uint8_t Function1Uint8(uint8_t x) { return x + 42; }
 
diff --git a/pkgs/ffigen/test/native_test/native_test.dart b/pkgs/ffigen/test/native_test/native_test.dart
index 63d1824..baa1bac 100644
--- a/pkgs/ffigen/test/native_test/native_test.dart
+++ b/pkgs/ffigen/test/native_test/native_test.dart
@@ -50,6 +50,10 @@
         rethrow;
       }
     });
+    test('bool', () {
+      expect(bindings.Function1Bool(true), false);
+      expect(bindings.Function1Bool(false), true);
+    });
     test('uint8_t', () {
       expect(bindings.Function1Uint8(pow(2, 8).toInt()), 42);
     });
diff --git a/pkgs/ffigen/test/native_test/native_test_bindings.dart b/pkgs/ffigen/test/native_test/native_test_bindings.dart
index 28a2bce..c5cdd81 100644
--- a/pkgs/ffigen/test/native_test/native_test_bindings.dart
+++ b/pkgs/ffigen/test/native_test/native_test_bindings.dart
@@ -11,6 +11,19 @@
   /// The symbols are looked up in [dynamicLibrary].
   NativeLibrary(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
 
+  bool Function1Bool(
+    bool x,
+  ) {
+    _Function1Bool ??= _dylib
+        .lookupFunction<_c_Function1Bool, _dart_Function1Bool>('Function1Bool');
+    return _Function1Bool(
+          x ? 1 : 0,
+        ) !=
+        0;
+  }
+
+  _dart_Function1Bool _Function1Bool;
+
   int Function1Uint8(
     int x,
   ) {
@@ -301,6 +314,14 @@
   }
 }
 
+typedef _c_Function1Bool = ffi.Uint8 Function(
+  ffi.Uint8 x,
+);
+
+typedef _dart_Function1Bool = int Function(
+  int x,
+);
+
 typedef _c_Function1Uint8 = ffi.Uint8 Function(
   ffi.Uint8 x,
 );