モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

Huion H610ProV2を再購入したらハマった話 & OpenTabletDriver導入メモ

Huion社の板タブH610ProV2、これがマイフェイバリット極まる感じでして。 後継機種が少しづつ消えていく中、いつまで売っているんだコレは…消える前に買っておかねば!って感じで。 最近、ペンタブ本体を机に置くと再接続扱いになってしまうようになった、現在使用中のH610ProV2が完全に壊れる前に買ってしまいました。Amazonセールで安かったですし。

なお、H610ProV2の描き心地は極上…と自分では思ってるんだけど、多分人によっては全く違う感想になると思います。 表面はつるつる気味で、ペン先はやや深めの沈み込み(言うほどではない)なので、ワコムのような紙に描いてるようなザラザラ感とか、沈み込みほぼゼロなものを求める人には向いていない。 私はその正反対なようです。

さてこのH610ProV2の新品、届いたためにさっそくUbuntu mate 20.04のマイメインPCに繋いでみたら、数年前に買った現用の物と違い、カーソルやkritaが反応しない

全くカーソルが動いてくれません。よって、線も描けない😭

dmesgでは反応しているし、xinputの一覧には出ているのだけど。 また、evtestでも入力データは来ている。

いきなり初期不良か?と思ってwindows機(huionドライバ導入済み)に繋いだら…カーソルは動きました。 しかし、筆圧が全く出てこない😭 って、これはwindows ink周りのせいだったらしく、windows ink使うようにしたらOKでした。

つまり、初期不良ではない。 色々調べた所、H610ProV2のファームウェアが最新であるとこういう症状になるようです。(なお、ubuntu 22.04でも状況は変わりません)

そこで、Linuxでこの状況の対処してお絵描きに普通に使う方法を3つほど記しておきます。

1. Huion公式Linux版ドライバを使う

一番簡単な解決策。これであれば、生のxinputでは何故か反応しない傾きも反応するし、画面マッピングも簡単。

しかし、これには幾つか問題点があるのです…

kritaで中ボタンが反応しなくなる問題

公式ドライバではkritaでスタイラスのサイドボタンがマウスとして扱われてしまい、ペンタブスタイラスのボタンとしては扱われなくなってしまいます。 私はサイドボタンを激しく使い、キャンバスの「手のひらスクロール」のみならず、shift + サイドボタン+浮かしてドラッグでキャンバス回転・ ctrl+サイドボタン+浮かしてドラッグでキャンバスズームという設定にしているので、これが使えないと話にならないのですな。

一応、メニューの 設定 → Kritaの設定を変更→ タブレットの設定 から 「マウスイベントを使用して右クリックと中クリックを行う」をチェックすれば、公式ドライバでも中ボタンを使えるようにはなります。

しかし…この状況ではごくごく微妙に中ボタンの反応が悪いために、速い操作や操作ミスの時にキャンバススクロール等のツールが誤認識され、スクロールモードなどに入ったままロックされてしまう現象が起きます。そうした時は、一端手動でフリーハンドブラシなどに切りかえる必要があり…コレが稀にストレス。

いつまでもbeta問題

最新が22年でいつまでもbetaが取れません。普通に安定して動いてるしまったく問題はないのだけど、正直金にならないものをいつまで続けてくれるのか…というのは心配な所。

2.OpenTabletDriverを使う方法

OpenTabletDriverなる有り難い有志の皆様が作ったオープンでマルチプラットホームな最強のドライバがあります。これを使えば問題ない…のですが、インストールに非常に手間取りましたのでメモ。 正直、dotnetを使うのは止めて欲しい。

公式の手順に従っても動作しない問題

公式のFAQにある手順に従っても、動作しませんでした。 (なお、メインのubuntu mate 20.04ではなく、 サブマシンのxubuntu 22.04マシンで行いました)

  • まず、sudo apt install opentabletdriver.deb となっていますが、これだと_aptのユーザーがなんとか言ってインストールが正常に終了しませんでした。素直にsudo dpkg -i opentabletdriver.debが吉。
  • 上記の手順に従わない(従えない)せいか、otd-guiGUI設定ツールが起動する筈ですが、反応せず。ターミナルから実行してみたら、dotnetが見つからないというエラーが出てました。(aptでのdotnetは説明に従いインストール済み)
  • これについては、MSから6.0.413のtar.gzを拾ってきて、MSの説明に従ってインストール & 環境変数設定した所動作しました。
  • 手順通りsystemdのopentabletdriver.serviceをenableしていてもexitしてしまい動作していないため、otd-guiが動いてもdaemonに接続できないと言って終了。
  • otd-guiが動くようになった時点で、opentabletdriver.serviceをstop/startを繰り返したら動くようになりましたが、OS再起動した後はdeadになっていて動作してくれない
  • 仕方ないので、簡単なスクリプトを書いてxfce自動起動プログラムに設定した所、動くようになってくれました。

拙い急造スクリプトですが一応、記しておきます。

#!/bin/bash

svr="opentabletdriver"

for i in `seq 0 10`;
do
cnt=`systemctl --user status $svr | grep -c "dead"`
    if [ "$cnt" = "1" ];then
       echo "dead, reset"
       systemctl --user stop $svr
       sleep 2
       systemctl --user start $svr
       sleep 2
    else
      echo "okay"
      exit 0
    fi
done

OpenTabletDriver自体は非常に操作性も良く、設定もわかりやすく、とても有り難いのですが…正直、dotnetを使うのは面倒でトラブルの種でしかないので止めて欲しい。大事なことなので二度書きました。

とは言え、マルチプラットホームである所を考えればC#/dotnetは妥当な選択ではあるのだけど。

3.xorg.conf.dを使いwacomドライバに設定する方法

X11環境ではwacomドライバが使えるので、これにすり替えてしまうという方法。

これが殆ど何もする必要がなく一番簡単かもしれませんが、これにはペンの傾きが無視されるようになるという問題点が… ま、まぁ、傾きなんて正直使わないけど。

最初はこれを設定してもダメだった気がしたのですが、何故か今は動いています…不思議?!(単に、マルチモニタの別の場所でカーソルが動いていたので、気づかなかっただけかも)

なお、以前のH610ProV2のUSB-IDは「256c:006c」でしたが、新しいものは「256c:0064」に変わっているようです。

以下の内容のファイルを /usr/share/X11/xorg.conf.d/99-my-huion.conf とでもして、再起動すればそれ以降はxsetwacomコマンドで管理出来るようになります。

Section "InputClass"
        Identifier "Huion H610ProV2-new"
        MatchUSBID "256c:0064"
        Driver "wacom"
        #  Option "tilt" "on" #どうも 効果が無いっぽい。傾きは相変わらず得られない(kritaで試した)
EndSection

ワコムドライバでxsetwacomを使う時の、生のxinputに対する利点は、筆圧を調整可能な点ですね。ただしこれはkrita側で出来てしまうので、接続されているペンタブがこれだけであればあまり意味は無いのですが。

液タブと板タブを両方繋げて使い分けるような時は意味が出てきます。

いつまでX11だ問題

これは言うまでもなくwayland環境になったら使えないために、waylandが使いたくなったら/ waylandだけの時代が来たら、ダメな気がします。

OpenTabletDriverはwayland対応しているようなので、あちらは問題ないですなぁ。どうしたものだか。

LuaのLÖVEに挑戦してみた

ツイッターのとろらぼ@TororoLabさんがLÖVEに目覚めたようなので、私も真似して自分でもハローワールドしてみました☺️

テキトーに公式チュートリアル(だと思う)からコピペ改変〜

function love.load()
    local image = love.graphics.newImage("duck1.png")
    duck = { x=100, y=300, angle=0,image=image,ox=image:getWidth()/2,oy=image:getHeight()/2 , step=0 }
end


function love.draw()
    love.graphics.print('Hello World!', 400, 300)
    love.graphics.draw(duck.image, duck.x-32, duck.y-32);
end

function love.update(dt)
    duck.x = 400 + math.cos(duck.step) * 400;
    duck.step = duck.step + 0.01;
end

スプライトは、いい無料アセットが出てこなかったのでチャチャッと自作です。まぁハローワールドだからね…

モチーフは大阪湾に時折現れる巨大ラバーダックです。あれのぬいぐるみというかラバーダック欲しいんですけどね、来たときにしか売ってくれないっぽいですな

使用したスプライト

なんの変哲もないハローワールドですが地味に面白い スマホで動くかどうかはやってませんがLOVE自体は動くわけですし、可能性を感じてしまいました

ではでは☺️

Xubuntu 22.04.2にpytorchを入れてCUDA対応させてみた話

AIってもんをちょっと触って見ようかな〜と思って、インストールが多少簡単っぽいpytorchにしよう!さらに、折角だからCUDAで動かしたいと。GeForce 1650しか持ってないけどお試しならこれでいいだろ…的な事を思い立ちましたが…

検索すると、やれdpkgだのanacondaだのと…

もうちょっと簡単に入らないものか😡

と思って取り敢えずaptだけでやってみましたが…案の定、動かず (torch.cuda.is_available()が何やってもFalse)

そこでふとひらめいて aptの方のpytorchを削除。pipからpip install torchでインストールしてみたら

普通にtorch.cuda.is_available()がTrueになりました😳

なお、多少色々重複してますが、aptで入れたのは、nvidia-cuda-dev nvidia-cuda-toolkit nvidia-cuda-gdb nvidia-cuda-toolkit-gcc nvida-cudnn となっておりました。また、ドライバは全部プロプライエタリな方のver530を入れています(最初、open kernelの方を入れてみたらvulkanのvkcubeですら挙動が怪しかった…)

本当に動いてるかどうか、拾ったサンプルで試した限りでは動いているようです(何処から拾ってきたか忘れた…😅) デバイスを作る部分の下に確認表示を自分で追加しましたが

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print("hello torch - device is %s" % str(device.type)) # GPUで動いている場合、 「hello torch - device is cuda」になるはず。

これはちゃんとdevice is cudaとなっております。

取り敢えず何かダメになるまではこの環境で行ってみよう…

gambas3のqt4でImageに描画する方法

ChatGPTに聞いても嘘しか返ってこなかったので😡

最近、gambasってもしかして良いんじゃないか?と思ってます。速いし、型があるし、IDEの完成度もなかなかです。 しかし、マイナーなので資料に乏しいのが悩みどころ。

ChatGPTに聞くと一応返してくれるのですが特にPaint周りは嘘ばっかでした…😭

  Paint.Begin(img)

  Dim testcol As Integer

  testcol = Color.SetRGB(testcol, 255, 128, 0, 128) ' 半透明のオレンジ
  
  Paint.LineWidth = 2
  Paint.FillRect(0, 0, 256, 256, testcol) 
  
  Paint.Brush = Paint.Color(&H00FF0000) ' 完全に不透明な真っ赤
  Paint.MoveTo(10, 10)
  Paint.LineTo(246, 246)
  Paint.Stroke()
  
  Paint.End()
  • Paint.Begin(image) 〜 Paint.End()で囲みます
  • Sttrokeで線描画、 Fillで塗りつぶしは普通のqtと似てます
  • なんと、アルファ値の大小が通常と逆です。 一般的には8ビットARGBだと255で不透明ですが、0で不透明です。

このアルファ値の大小で悩まされましたですね。完全に不透明と思い込んでいたものが全く描画されなくて…(透明なんだから、当たり前だけど)

一瞬、int32なので、負の値周りのバグかと思ってしまいました。 実はこの問題、SDL2でも悩まされていたのです。 バグでなくて本当によかった。

なお、gambas3のIDEは日本語コメントも受付ますが、日本語文字列の描画が怪しいので英語でコメント書いてます。上のサンプルでのコメントは、はてな側で書き加えたものです。 一方、似たようなRAD的IDE的な、FreepascalのLazarusも捨てがたいのですが、こちらは日本語コメントを全くインライン編集出来ません(クリップボード経由のコピペは出来る)

一長一短ですというか、どうせ英語でコメント書くならもうLazarusでも良いかな…と思わなくもないです。ただワタクシPascalはかなり素人なので、その点はキツイですが…しかし、むしろGambasよりは資料多そうですね。

いずれゲームでも作ることで本格的に使い比べてみたい。

webフォントの記事が良いのが引っかからなかったのでメモ

ChatGPTに聞いても良いんだけど、ついつい習慣でググってしまいますな😅 っていうかChatGPTの答えは正しいのか正しくないのかイマイチ不明、っていうか私が聞くようなことは大抵正しく無いんですが…。 聞き方も悪いのかもしれない。

それはさておきwebフォントの記事で彷徨ってしまったので一応メモしておきます。 お題はwebフォントを描画する時にロード完了はどう知ればいいの?と言うやつ

これが「document.fonts.ready」を最初から知っていれば、それでググれば良い検索結果がヒットするのですが、知らずにググると中々それに巡り合わないという状況でした。うーむ…

ロード完了を待たないと案の定、フォントが適切に表示できる時と代替フォントで適当な奴が出てしまう時という状況が現れるわけです。

これ今どきは簡単で、 document.fonts.ready がpromiseなのでそれをthenすればいい、と。

googleのwebフォントサイトからコピペして、htmlは以下のような状況で、

<!DOCTYPE html>
<html lang="ja">
<head> <meta charset="UTF-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@700&family=Noto+Serif+JP:wght@700&display=swap" rel="stylesheet"> 
<style>
    body {
        font-family: 'Noto Sans JP', sans-serif;
        font-family: 'Noto Serif JP', serif;        
    }
</style>
</head>
<body>
<canvas id="canvas2D" width="640" height="480"></canvas> <!-- 文字表示用レイヤ -->
</body>
</html>

(注: ちゃんとbodyにでもcssのfont-family付けとかないと描画出来ないようです)

スクリプトの方は、最初は、コレで動いてると思ってたんですが…

    document.fonts.ready.then( ()=> {
        console.log("--- webfont ready.");
        let cvs = document.getElementById("canvas2D");
        let ctx = cvs.getContext('2d');
        ctx.font = "28px Noto Serif JP"; // 明朝
        ctx.fillStyle = "#F00";
        ctx.fillText("完全にTESTですよ",  10, 10 + 28);
        ctx.font = "28px Noto Sans JP"; // ゴシック
        ctx.fillText("TESTなのです",  10, (10+28)*2);
    });

みたいな感じで指定したwebフォントの読み込みを待って描画できると。

しかし、chromiumで動かず😅 初回、必ず代替フォントで描画されてしまいます。 環境としては

リロードすると正しく描けるのですけどね…色々やって最終的にこうなりました。

  • document.fonts.ready.then に加え、 onloadingdoneイベントを併用する
  • そして、双方から同じ関数を呼び出す等して二回描くと。
  • firefoxでは大丈夫なようだけど、chiromiumでは片方だけだと問題が起きる。
  • onloadingdoneだけにすると、そもそもイベントが発火せず、何も描画されない。
  • document.fonts.ready.then() だけにすると、前述の用に初回は代替フォントで描かれてしまう
function drawText(fontFaceSet) {

        if (fontFaceSet != null) {
        console.log(fontFaceSet.check('28px "Noto Sans JP"'));
        console.log(fontFaceSet.check('28px "Noto Serif JP"'));
        }

        let cvs = document.getElementById("canvas2D");
        let ctx = cvs.getContext('2d');
        ctx.font = "28px Noto Serif JP"; // 明朝
        ctx.fillStyle = "#F00";
        ctx.fillText("完全にTESTですよ",  10, 10 + 28);
        ctx.font = "28px Noto Sans JP"; // ゴシック
        ctx.fillText("TESTなのです",  10, (10+28)*2);

}

window.addEventListener('load', function () {

    // chromiumでは両方やらないと初回は正しく描画出来ない(リロードすると出来る)
    document.fonts.addEventListener("loadingdone", (event) => {
        console.log("--- webfont loaded.");
        drawFonts(null);
    });
    
    document.fonts.ready.then( (fontFaceSet)=> {
        console.log("--- webfont ready.");
        drawFonts(fontFaceSet);
    });

}

chromiumではfontFaceSet.checkは両方共falseになってしまいます。firefoxでは28px "Noto Serif JP"のほうだけfalse。

いろいろ、謎。

超久々にRaspberryPi pico wでhttpサーバなLチカ!

超久々ですが、RaspberryPi Pico Wを入手できたため、micropythonにて公式サイトから拾えるraspberry-pi-pico-python-sdk.pdfにあるサンプルを参考にhttpサーバでLチカしてみました。 単にLチカしてもアレなので多少工夫を加えてあります

  • Thonnyで開発したのですが、そのままだと再起動するたびにEADDRINUSEが出て引っこ抜く事になるためにsetsocketoptなどしてあります
  • その他、丁寧にfinally節でcloseしてます
  • ledピンはPin("LED", Pin.OUT)で得るのが無難なようです
  • 何故かルータがいきなり初手でリクエストなしでアクセスしてきて開きっぱなしにするため、timeoutを付けました (不気味…)
  • サーバっぽくos.fork()で処理したかったんだけどunixにしか無いんですなコレ…multiprocessingとかthreadはmicropythonで出来るのかどうか知らないので、手っ取り早くタイムアウトだけにしました

使い方は、本家のようにurlに/light/on で点灯、 /light/offで消灯ですが、それに加え、 /light/0x0f などとすると、8ビットの数値のビットパターンで、0だと「短く点灯」1だと「普通に点灯」で点滅により16進数を表現します。

さらに、/quitでサーバをシャットダウンしてwifiを切断します。

さすがmicroとはいえpythonだけあって色々簡単に出来て、面白いですね😆

なお、Thonnyでインタプリタを設定することでPico Wに直で開発中のコードを送り込んで実行することが可能なのですが、ubuntu 20.04.6で入るThonny 3.2.7にはPico Wの項目がありません…(当たり前かw)

しかしながら、これはesp32を指定すれば問題なく実行出来ました。まぁpythonだし大丈夫だろ…ということで😁

import network
import socket
import time
import os

from machine import Pin

def send_response(cl, msg):
    global html
    response = html % msg

    cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    cl.send(response)
    
led = Pin("LED", Pin.OUT)

ssid = 'your ssid'
password = 'your password'

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)

html = """<!DOCTYPE html>
<html>
    <head> <title>Pico W</title> </head>
    <body> <h1>Pico W</h1>
        <p>%s</p>
    </body>
</html>
"""

max_wait = 10
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print('waiting for connection...')
    time.sleep(1)

if wlan.status() != 3:
    raise RuntimeError('network connection failed')
else:
    print('connected')
    status = wlan.ifconfig()
    print( 'ip = ' + status[0] )

addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # To avoid EADDRINUSE
s.bind(addr)
s.settimeout(5) # Some router accesses without request and connect forever.
s.listen(1)

print('listening on', addr)

# Listen for connections

unit = 0.5 # led blink unit in second
try:
    while True:
        try:
            cl = None # for finally block.
            cl, addr = s.accept()
            print('client connected from', addr)
            ipaddr,port = addr
            print("waiting request...")
            request = cl.recv(1024)
            print(request)

            request = str(request)
            reqtail = request.find(' HTTP/1.1')
            if reqtail < 0:
                print("no request body")
                continue
            reqbody = request[6:reqtail].strip()
            print(reqbody)
            
            led_status = None
            if reqbody == '/quit':
                print("quit server.")
                send_response(cl, "Server shutdown.")
                break
            elif reqbody == '/light/on':
                led_status = True
            elif reqbody == '/light/off':
                led_status = False
            elif reqbody.startswith('/light/0x'):
                led_status = reqbody[7:11]
                
            if led_status is None:
                print("invalid request.")
                stateis = "invalid"
            elif led_status == True:
                led.value(1)
                stateis = "led on"
            elif led_status == False:
                led.value(0)
                stateis = "led off"
            else:
                # burst blink mode.
                try:
                    value = int(led_status, 16)
                    stateis = "blink : %s" % led_status
                    for i in range(8):
                        led.value(1)
                        cv = (value >> i) & 1
                        if cv == 0:
                            time.sleep(unit/3)
                        else:
                            time.sleep(unit)
                        led.value(0)
                        time.sleep(unit)
                            
                except ValueError:
                    stateis = "Failed to convert : %s" % led_status              

            send_response(cl, stateis)

        except OSError as e:
            pass
        finally:
            if cl is not None:
                print('connection closed')
                cl.close()
                cl = None

finally:
    s.close()
    wlan.disconnect()

Krita4でレイヤーリストをマウス中ボタンドラッグでスクロールする方法

ちなみにマウス中ボタン=ペンタブスタイラスの下サイドボタンです。

キャンバスでのマウスボタン割当のデフォルトは、マウス中ボタンでのスクロールと思います。 もしかすると自分でカスタマイズした結果かもですが…よく覚えていません(^^;

ともかくこれを設定すると、キャンバスでのスクロールとレイヤーリストのスクロールのインターフェースが共通化されて非常に感覚的に使えるようになるのですな

さてやり方は…というと

Configure Krita-> General -> Kinetic Scrolling(needs restart) をONにして、「On Middle-Click Drag」を選択。

f:id:dothiko:20200509015214j:plain

Sensitivityは適当に…私は85%にしています。別に根拠のある数字ではないです。

ところでなんでメニューを英語のまま使っているのか…というと単純に検索の問題です。 何か調べたい時に日本語だと英語の情報にリーチ出来ない…というだけでなく、そもそも、調べたい言葉が英語でなんと書かれているのかを日本語訳から一発で知ることは難しいのです。だから検索が二重に困難になるのですね。 むしろ、Kritaを日本語で使うのは上級者向きの気がしなくもないです。

ではでは〜