Skip to content

Commit

Permalink
Begin looking more deeply at BlSpace (internal working and attributes).
Browse files Browse the repository at this point in the history
  • Loading branch information
rvillemeur committed Jul 2, 2024
1 parent 80dbbdc commit 6657e71
Show file tree
Hide file tree
Showing 2 changed files with 321 additions and 1 deletion.
320 changes: 320 additions & 0 deletions Chapters/bloc/BlocInternals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
## relation between BlUniserse, BlSpace, BlHost

BlUniverse contain multiple BlSpace.
However, we call `BlElement openInNewSpace`
This will open a new OS Windows, with our scene


We begin with `BlElement >> openInNewSpace`
or `BlElement >> openInSpace` which are equivalent.

```smalltalk
BlElement >> openInNewSpace
"Add self to a new BlSpace and show it. Answer such space."
| aSpace |
aSpace := BlSpace new.
aSpace root addChild: self.
aSpace show.
^ aSpace
```

Contrary to GTookit, BlSpace is not a subclass of BlElement It has, however,
a lot of specific responsabilities:

```smalltalk
BlSpace >> initialize
super initialize.
id := UniqueIdGenerator generateUniqueId.
host := BlHost pickHost. "Return all supported Bloc host classes, sorted by priority (lowest first)."
nextPulseRequested := true.
session := Smalltalk session.
elementsNeedingPaint := Set new.
elementsNeedingLayout := Set new.
eventDispatcher := self defaultEventDispatcher.
eventListener := self defaultEventListener.
self initDispatcher.
mouseProcessor := BlMouseProcessor space: self.
focusProcessor := BlFocusProcessor space: self.
keyboardProcessor := BlKeyboardProcessor space: self.
tasks := BlSpaceTaskQueue space: self.
time := BlTime real.
frame := BlSpaceFrame new.
rootElement := self defaultRoot.
self extent: self defaultExtent.
self resizable: true.
self borderless: false.
self fullscreen: false.
self fullsize: false.
self title: self defaultTitle.
self focused: false.
self updateCursor: Cursor normal.
self root space: self
```

```smalltak
BlSpace >> show
"Open me in a window and show it to the user"
"delegate showing work to the Universe"
(BlParallelUniverse forHost: self host class) openSpace: self
```

BlHost and other possibilities:
- BlOSWindowSDL2Host (define its own pulse loop)
- BlMorphicWindowHost (use morphic loop)

We can define in the space which host to use:
```
BlSpace new
host: host;
title: host asString;
extent: 200 asPoint;
show
```

By default, it'll use `BlOSWindowSDL2Host`

`BlOSWindowSDL2Host` has its own pulse loop

```smalltalk
BlHostPulseLoop >> defaultPulsePeriod
^ 15 milliSeconds
```

BlOSWindowSDL2Host will create it own its own element and renderer.

Host is linked to a renderer and a way to catch keyboard event.

```smalltalk
BlOSWindowSDL2Host >> createHostSpace
^ BlOSWindowSDL2HostSpace new
```

```smalltalk
BlOSWindowSDL2Host >> keyboardKeyTable
^ BlOSWindowSDL2KeyboardKeyTable default
```


## Pulse
```smalltalk
BlSpaceFrameDrawingValidationPhase >> runOn: aSpace
aSpace hasHostSpace
ifFalse: [ ^ self ].
aSpace hostSpace needsRebuild
ifTrue: [ ^ self ].
aSpace hostSpace hasResized
ifFalse: [ ^ self ].
aSpace invalidateAll.
aSpace hostSpace initializeRenderer
```

Renderer is defined by
```smalltalk
BlOSWindowSDL2Host >> createHostSpaceFor: aSpace
"Create and assign a new host space for given bloc space"
| aHostSpace|
aHostSpace := self createWindowSpaceFor: aSpace.
aHostSpace renderer: BlHostRenderer preferableClass new.
aSpace hostSpace: aHostSpace
```

`BlHostRenderer preferableClass` return `BARenderer`

```smalltalk
BARenderer >> initializeForHostSpace: aBlHostSpace
"Initialize this renderer for a given host space.
Please note, that it I be called multiple times"
self initialize.
session := Smalltalk session.
"we should mark it as a current one before initializing a canvas as it may rely on opengl context"
aBlHostSpace makeCurrent.
surface := aBlHostSpace newBlHostRendererSurface.
surfaceRenderer := surface newSurfaceRendererOn: aBlHostSpace.
spaceRenderer := surface newSpaceRendererOn: self.
textMeasurer := BASpaceTextMeasurer new
spaceRenderer: spaceRenderer;
yourself
```

new BLHostRendererSurface ->
```smalltalk
BlOSWindowSDL2HostSpace >> newBlHostRendererSurface
^ BlHostRendererBufferSurface newForHostSpace: self
```

`surface newSpaceRenderOn: self will lead to:

```smalltalk
BlHostRendererBufferSurface >> createSpaceRendererOn: anObject
^ anObject createBufferSpaceRenderer
```

which will return `BABufferSpaceRenderer new`

Which will end to

```smalltalk
BABufferSpaceRenderer >> initializeForSurface: aBlHostRendererBufferSurface
self
initializeForSurface: aBlHostRendererBufferSurface
and: (AeCairoImageSurface
newForData: aBlHostRendererBufferSurface buffer
extent: aBlHostRendererBufferSurface extent
stride: aBlHostRendererBufferSurface stride
format: AeCairoSurfaceFormat argb32)
```

## BlSpace frame

`BlSpaceFrame class`,
```smalltalk
BlSpaceFrame >> initializePhases
self addPhases: {
BlSpaceFrameIdlePhase new.
BlSpaceFrameHostValidationPhase new.
BlSpaceFrameTaskPhase new. " animation as an example "
BlSpaceFrameEventPhase new. " generate BlMouse events from host mouse event as an example "
BlSpaceFrameDrawingValidationPhase new.
BlSpaceFrameLayoutPhase new. " layouting all elements that requested layout "
BlSpaceFrameDrawingPhase new " drawing invalidate elements "}
```

This sequence will repeat itself and redraw invalidate elements in the end.

```smalltalk
BlSpaceFrameDrawingPhase >> runOn: aSpace
aSpace
dispatchTimeEvent: BlSpaceRenderEndEvent
during: [ :theSpace | BlSpaceRenderSignal for: theSpace block: [ theSpace render ] ]
```

Which calls:

```smalltalk
BlSpace >>> render
"Render this space in my host window if it is assigned, otherwise do nothing"
self
hostSpaceDo: [ :aHostSpace | aHostSpace render: self ]
"if there is no host we should clear dirty areas, otherwise
it may lead to memory leaks"
ifAbsent: [ self clearDirtyElements ]
```

```smalltalk
BlHostRenderer >> render: aHostSpace
"Render a given space according to its dirty areas.
Note: if there are no dirty areas nothing will happen, including window
or canvas update."
| aSpace shouldUpdateEveryFrame isRenderNeeded |
aSpace := aHostSpace space.
shouldUpdateEveryFrame := surface shouldUpdateEveryFrame.
isRenderNeeded := aSpace hasDirtyElements.
(shouldUpdateEveryFrame or: [ isRenderNeeded ])
ifFalse: [ ^ self ].
self isValid
ifFalse: [ ^ self ].
[ | theDamagedRectangles |
aHostSpace makeCurrent.
theDamagedRectangles := isRenderNeeded
ifTrue: [ spaceRenderer renderSpace: aSpace ]
ifFalse: [ #() ].
surface performUpdates.
surfaceRenderer renderSurface: surface damaged: theDamagedRectangles ]
ensure: [ self finishedRender: aSpace ]
```

Which finally call:

```smalltalk
BASpaceRenderer >> renderSpace: aBlSpace
"Render a space and return a collection of damaged rectangles"
aBlSpace aeFullDrawOn: aeCanvas.
^ #()
```


## BlSpace drawing sequence

It'll begin with

```smalltalk
BASpaceRenderer >>renderSpace: aBlSpace
"Render a space and return a collection of damaged rectangles"
aBlSpace aeFullDrawOn: aeCanvas.
^ #()
```

```smalltak
BlSpace >> faeFullDrawOn: aeCanvas
self root aeFullDrawOn: aeCanvas
```

Which then call BlElement render call

```
BlElement >> aeFullDrawOn: aCanvas
"Main entry point to draw myself and my children on an Alexandrie canvas."
self aeDrawInSameLayerOn: aCanvas.
self aeCompositionLayersSortedByElevationDo: [ :each | each paintOn: aCanvas ]
```


```smalltalk
BlElement >> aeCompositionLayersSortedByElevationDo: aBlock
(self isTransparent or: [ self isVisible not ])
ifTrue: [ ^ self ].
self wantsSeparateCompositingLayer ifTrue: [
self hasCompositionLayer ifFalse: [
self compositionLayer:
(BAAxisAlignedCompositionLayer newFor: self) ].
aBlock value: self compositionLayer ].
self flag: #todo. "extract to allow overriding, exactly as aeDrawChildrenOn:"
self children sortedByElevation do: [ :each |
(each aeCompositionLayersSortedByElevationDo: aBlock) ]
```

Drawing is separated into layers.
2 changes: 1 addition & 1 deletion pillar.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"base_url": "",
"site_name": "Graphics in Pharo",
"title": "Graphics in Pharo",
"attribution": "R. De Villemeur, S. Ducasse and A.Plantec",
"attribution": "R. de Villemeur, S. Ducasse and A.Plantec",
"series": "Square Bracket tutorials",
"keywords": "graphics, canvas, bloc, toplo, widgets",

Expand Down

0 comments on commit 6657e71

Please sign in to comment.