C# で GTK の GNOME ウインドウで Hello World する

# C# で GTK の GNOME ウインドウで Hello World する

こんにちは、@studio_meowtoon です。今回は、WSL の Ubuntu の C# / .NET 環境で Linux の GNOME アプリを開発する方法を紹介します。  
![csharp_on_ubuntu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/23c7c71f-e5f1-3bb8-45b6-60ad19e41238.png)

## 実現すること

Windows 11 のデスクトップに WSL Ubuntu 22.04 の GNOME GUI カスタムアプリのウインドウを立ち上げます。

## 技術トピック

### .NET とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [Microsoft .NET](https://dotnet.microsoft.com/ja-jp/learn)

.NET は、マイクロソフトによって開発されたオープンソースのクロスプラットフォーム開発フレームワークです。  

|キーワード|内容|
|:--|:--|
|クロスプラットフォーム対応|.NET は、Windows、macOS、Linux、Android、iOS など、複数のプラットフォームで実行されるように設計されています。これにより、同じコードを複数のプラットフォームで共有できます。|
|豊富なライブラリ|.NET には、豊富なライブラリが含まれています。これにより、開発者は、簡単にデータベースやネットワーク、GUIなどの機能を組み込むことができます。|
|マルチ言語サポート|.NET は、複数のプログラミング言語 (C#、F#、Visual Basic など) をサポートしています。開発者は、自分の好きな言語でアプリケーションを開発できます。|
|高速性|.NET は、JIT コンパイラによって実行されるため、高速に動作します。また、メモリ管理が自動的に行われるため、開発者はメモリ管理に関する問題を心配する必要がありません。|
|セキュリティ|.NET には、セキュリティに関する多数の機能が含まれています。たとえば、コードアクセスセキュリティ (CAS) があり、アプリケーションに対する不正なアクセスを防止することができます。|
|まとめ|これらの特徴とメリットにより、.NET は、クロスプラットフォーム開発において人気のあるフレームワークの一つとなっています。|

</details>

### GNOME とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [GNOME (グノーム、ノーム)](https://developer.gnome.org/)

GNOME は、Linux や Unix 系オペレーティングシステム上で動作する、オープンソースのデスクトップ環境です。

|キーワード|内容|
|:--|:--|
|クロスプラットフォーム対応|GNOME は、Linux や Unix 系オペレーティングシステム上で動作するため、多数のプラットフォームに対応しています。また、Windows や macOS 上でも動作する GNOME のフォークである Cygwin/X や XQuartz があります。|
|カスタマイズ性|GNOME は、高度なカスタマイズが可能です。ユーザーは、アプリケーションのレイアウトやテーマ、アイコン、フォントなどを簡単に変更できます。|
|ユーザーフレンドリー|GNOME は、シンプルで使いやすいユーザーインターフェースを提供しています。初心者でも簡単に操作できるように設計されており、視覚的にも美しいデザインが特徴です。|
|高い拡張性|GNOME は、多数のアプリケーションやツールを提供しており、これらを拡張することができます。また、GNOMEシェル拡張を使用することで、ユーザーは独自の機能を追加することもできます。|
|オープンソース|GNOME は、オープンソースであり、自由に改変・配布ができるため、開発者は自分の好きなようにカスタマイズすることができます。|
|まとめ|これらの特徴とメリットにより、GNOME は、Linux や Unix 系オペレーティングシステム上で広く使用されているデスクトップ環境の一つとなっています。また、クロスプラットフォーム対応やカスタマイズ性の高さから、Windows や macOS 上でも利用されることがあります。|

</details>

### GTK とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [GTK](https://www.gtk.org/)

GTK は、GNOME デスクトップ環境で使用されるウィジェット・ツールキットです。  

|キーワード|内容|
|:--|:--|
|クロスプラットフォーム対応|GTK は、クロスプラットフォーム対応しています。Linux や Unix 系オペレーティングシステムはもちろん、Windows や macOS、さらには Android や iOS などでも利用できます。|
|フリー・オープンソース|GTK は、フリーかつオープンソースであるため、誰でも自由に利用・改変・再配布することができます。|
|多言語対応|GTK は、多数のプログラミング言語で利用できます。C、C++、Python、Java などの言語をサポートしています。|
|ウィジェットの豊富さ|GTK は、多数のウィジェットを提供しています。ボタン、テキストボックス、ラベル、プログレスバー、スピンボタンなど、様々な種類のウィジェットを利用することができます。|
|GNOME との関連|GTK は、GNOME デスクトップ環境で利用されており、GNOME アプリケーションの開発に必要不可欠なツールキットとなっています。|
|まとめ|これらの特徴とメリットにより、GTK は、クロスプラットフォームで利用できるツールキットとして、多数のアプリケーション開発者に利用されています。また、GNOME デスクトップ環境の開発にも欠かせない存在となっています。|

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

.NET SDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/d68031c91462f6808dff)

```sh
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
```

</details>

## .NET でコンソールに "Hello World" を表示する

開発環境を確認する目的でまずコンソールアプリを作成してみます。

コンソールアプリを作成します。  
※ CUIApp がアプリ名です。  

```sh
$ cd ~/tmp
$ dotnet new console -o CUIApp -f net7.0
```

コンソールアプリをビルド・実行します。

```sh
$ cd ~/tmp/CUIApp
$ dotnet run
Hello, World!
```

:::note info
ここまでの作業で、ターミナルに "Hello, World!" が表示されました😋
:::

.NET のコンソールアプリの内容を確認します。

```sh
$ cd ~/tmp/CUIApp
$ tree -I 'bin|obj'
.
├── CUIApp.csproj
└── Program.cs
```

プロジェクトファイルを確認します。

```sh
$ cat CUIApp.csproj
```

```xml:MyApp.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>
```

プログラムを確認します。

```sh
$ cat Program.cs
```

```csharp:Program.cs
﻿// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
```

※ 参考: .NET 5.0 までは以下の内容でした。

```csharp:Program.cs ※ .NET 5.0
using System;
namespace MyApp {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine("Hello, World!");
        }
    }
}
```

:::note info
ここまでの作業で、dotnet コマンドによりコンソールアプリを新規作成、ビルド、実行することができました😋
:::

## .NET で GTK を使った GNOME ウインドウに "Hello World" を表示する

GTK をインストールします。

```sh
$ sudo apt update
$ sudo apt install libgtk-3-dev
```

:::note alert
エラーが発生する場合、以下のコマンドを試してみて下さい。
:::

```sh
$ sudo apt --fix-broken install
$ sudo apt update
```

Glade をインストールします。

:::note info
[Glade](https://glade.gnome.org/) は、GTK 用 GUI ビルダです。  
:::

```sh
$ sudo apt install glade
```

GtkSharp インストールします。

:::note info
[GtkSharp](https://github.com/GtkSharp/GtkSharp) は、GTK GUI ツールキットおよび GNOME ライブラリの .NET バインディングセットです。  
:::

```sh
$ dotnet new --install GtkSharp.Template.CSharp
```

新規 GTK GUIプロジェクト作成します。 
※ GTKApp がアプリ名です。  

```sh
$ cd ~/tmp
$ dotnet new gtkapp -n GTKApp
```

csproj ファイルを修正します。
※ .NET 7 に修正します。

```sh
$ vim GTKApp.csproj
```

```xml:GTKApp.csproj
<TargetFramework>net6.0</TargetFramework>
↓
<TargetFramework>net7.0</TargetFramework>
```

GUI アプリをビルド、実行します。

```sh
$ cd ~/tmp/GTKApp
$ dotnet run
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/ff978790-427f-28c3-f668-07473f11f840.png)

:::note info
Windows 11 のデスクトップに WSL の Ubuntu 22.04 の GNOME GUI ウインドウが立ち上がります！ 魔法のような話ですが本当です。 Microsoft さんありがとうございます😭  
:::

GTK GUI アプリの内容を確認します。

```sh
$ cd ~/tmp/GTKApp
$ tree -I 'bin|obj'
.
├── GTKApp.csproj
├── MainWindow.cs
├── MainWindow.glade
└── Program.cs
```

プロジェクトファイルを確認します。

```sh
$ cat GTKApp.csproj
```

```xml:GTKApp.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="**\*.glade" />
    <EmbeddedResource Include="**\*.glade">
      <LogicalName>%(Filename)%(Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="GtkSharp" Version="3.24.24.*" />
  </ItemGroup>

</Project>
```

メインプログラムを確認します。
  
```sh
$ cat Program.cs
```

```csharp:Program.cs
using System;
using Gtk;

namespace GTKApp
{
    class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
            Application.Init();

            var app = new Application("org.GTKApp.GTKApp", GLib.ApplicationFlags.None);
            app.Register(GLib.Cancellable.Current);

            var win = new MainWindow();
            app.AddWindow(win);

            win.Show();
            Application.Run();
        }
    }
}
```

ウインドウプログラムを確認します。

```sh
$ cat MainWindow.cs
```

```csharp:MainWindow.cs
using System;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;

namespace GTKApp
{
    class MainWindow : Window
    {
        [UI] private Label _label1 = null;
        [UI] private Button _button1 = null;

        private int _counter;

        public MainWindow() : this(new Builder("MainWindow.glade")) { }

        private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("MainWindow"))
        {
            builder.Autoconnect(this);

            DeleteEvent += Window_DeleteEvent;
            _button1.Clicked += Button1_Clicked;
        }

        private void Window_DeleteEvent(object sender, DeleteEventArgs a)
        {
            Application.Quit();
        }

        private void Button1_Clicked(object sender, EventArgs a)
        {
            _counter++;
            _label1.Text = "Hello World! This button has been clicked " + _counter + " time(s).";
        }
    }
}
```

UI ファイルを確認します。

```sh
$ cat MainWindow.glade
```

```xml:MainWindow.glade
﻿<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="3.18"/>
  <object class="GtkWindow" id="MainWindow">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Example Window</property>
    <property name="default_width">480</property>
    <property name="default_height">240</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_left">4</property>
        <property name="margin_right">4</property>
        <property name="margin_top">4</property>
        <property name="margin_bottom">4</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="_label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Hello World!</property>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="_button1">
            <property name="label" translatable="yes">Click me!</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="receives_default">True</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
```

:::note info
ここまでの作業で、Ubuntu に GTK、GtkSharp SDK をインストールして GTK の開発環境を構築しました。また、dotnet コマンドにより GTK GUI アプリを新規作成、ビルド、実行することができました。😋
:::

## VS Code で開発してみます

開発に必要なプラグインをインストールします。

※以前のこちらの記事を参考にして頂けます。

https://qiita.com/studio_meowtoon/items/e3aaef417f93afb79066

+ WSL
+ Remote - SSH
+ C#

ワークスペースを開きます。

※ Windows 11 の VS Code で、WSL Ubuntu 22.04 のソースコードを開きます。  

```sh
$ cd ~/tmp/GTKApp
$ code .
```

実行とデバッグ設定を行います。※.NET 5.0+ and .NET Core を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/e2c5e946-c979-b75d-6d27-fe20188093b1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/cc7b84c2-1f34-2b9f-cd93-a5d87617236b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/477967ef-7abf-9cb9-af2d-be582873f503.png)

VS Code からデバッグが可能となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/91ffc9c2-db89-075c-e56d-927646e1816b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/1f1e2fa5-732e-4254-c8b5-cefbeb5ca9d9.png)

:::note info
デバッグ実行ボタンを押すと、Windows 11 の VS Code で開いている WSL の Ubuntu 22.04 上の GUI プログラムのソースコードのブレークポイントで停止してくれます！ 魔法のような話ですが本当です。 Microsoft さんありがとうございます😭  
:::

## まとめ

Ubuntu 22.04 で GNOME GUI アプリを作成、ビルド、実行することが出来ました。

どうでしたか？ Window 11 の WSL Ubuntu に、C# / .NET の開発環境を手軽に構築することができます。ぜひお試しください。今後も .NET の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 参考資料

##### GTK

https://docs.gtk.org/gtk3/

##### GtkSharp

https://www.mono-project.com/docs/gui/gtksharp/